diff --git a/decorators.py b/decorators.py
index 79b70ac61aa037c3bdf4161fb98b63fb138a5775..17b73569653f0b514b910aa6ef152884252d2fb8 100644
--- a/decorators.py
+++ b/decorators.py
@@ -1,9 +1,10 @@
-from flask import flash
+from flask import request, flash, abort
 
 from functools import wraps
 
 from models.database import ALL_MODELS
 from shared import current_user
+from utils import get_csrf_token
 import back
 
 ID_KEY = "id"
@@ -90,3 +91,14 @@ def require_publish_right(require_exist=True):
 
 def require_admin_right(require_exist=True):
     return require_right("admin", require_exist)
+
+
+def protect_csrf(function):
+    @wraps(function)
+    def _decorated_function(*args, **kwargs):
+        token = request.args.get("csrf_token")
+        if token != get_csrf_token():
+            print(token, get_csrf_token())
+            abort(400)
+        return function(*args, **kwargs)
+    return _decorated_function
diff --git a/server.py b/server.py
index 32942750c3b2235035a59894358a14781aa8c19d..e7010e132cf2cad3a604c882abe1ab5c95646f7d 100755
--- a/server.py
+++ b/server.py
@@ -31,9 +31,9 @@ from shared import (
 from utils import (
     get_first_unused_int, get_etherpad_text, split_terms, optional_int_arg,
     fancy_join, footnote_hash, get_git_revision, get_max_page_length_exp,
-    get_internal_filename)
+    get_internal_filename, get_csrf_token)
 from decorators import (
-    db_lookup,
+    db_lookup, protect_csrf,
     require_private_view_right, require_modify_right, require_publish_right,
     require_admin_right)
 from models.database import (
@@ -89,6 +89,7 @@ app.jinja_env.filters["fancy_join"] = fancy_join
 app.jinja_env.filters["footnote_hash"] = footnote_hash
 app.jinja_env.tests["auth_valid"] = security_manager.check_user
 app.jinja_env.tests["needs_date"] = needs_date_test
+app.jinja_env.globals["get_csrf_token"] = get_csrf_token
 
 additional_templates = getattr(config, "LATEX_LOCAL_TEMPLATES", None)
 if additional_templates is not None and os.path.isdir(additional_templates):
@@ -336,6 +337,7 @@ def show_type(protocoltype):
 
 @app.route("/type/delete/<int:protocoltype_id>")
 @login_required
+@protect_csrf
 @db_lookup(ProtocolType)
 @require_admin_right()
 @require_modify_right()
@@ -382,6 +384,7 @@ def edit_reminder(meetingreminder):
 
 @app.route("/type/reminder/delete/<int:meetingreminder_id>")
 @login_required
+@protect_csrf
 @db_lookup(MeetingReminder)
 @require_modify_right()
 def delete_reminder(meetingreminder):
@@ -434,6 +437,7 @@ def edit_default_top(protocoltype, defaulttop):
 
 @app.route("/type/tops/delete/<int:defaulttop_id>")
 @login_required
+@protect_csrf
 @db_lookup(DefaultTOP)
 @require_modify_right()
 def delete_default_top(defaulttop):
@@ -445,6 +449,7 @@ def delete_default_top(defaulttop):
 
 @app.route("/type/tops/move/<int:defaulttop_id>/<diff>/")
 @login_required
+@protect_csrf
 @db_lookup(DefaultTOP)
 @require_modify_right()
 def move_default_top(defaulttop, diff):
@@ -649,6 +654,7 @@ def show_protocol(protocol):
 
 @app.route("/protocol/delete/<int:protocol_id>")
 @login_required
+@protect_csrf
 @db_lookup(Protocol)
 @require_admin_right()
 @require_modify_right()
@@ -663,6 +669,7 @@ def delete_protocol(protocol):
 
 @app.route("/protocol/etherpull/<int:protocol_id>")
 @login_required
+@protect_csrf
 @db_lookup(Protocol)
 @require_modify_right()
 def etherpull_protocol(protocol):
@@ -781,6 +788,7 @@ def upload_new_protocol_by_file():
 
 @app.route("/protocol/recompile/<int:protocol_id>")
 @login_required
+@protect_csrf
 @db_lookup(Protocol)
 @require_admin_right()
 @require_modify_right()
@@ -814,6 +822,7 @@ def get_protocol_template(protocol):
 
 @app.route("/protocol/etherpush/<int:protocol_id>")
 @login_required
+@protect_csrf
 @db_lookup(Protocol)
 @require_modify_right()
 def etherpush_protocol(protocol):
@@ -848,6 +857,7 @@ def update_protocol(protocol):
 
 @app.route("/protocol/publish/<int:protocol_id>")
 @login_required
+@protect_csrf
 @db_lookup(Protocol)
 @require_publish_right()
 def publish_protocol(protocol):
@@ -858,6 +868,7 @@ def publish_protocol(protocol):
 
 @app.route("/prococol/send/private/<int:protocol_id>")
 @login_required
+@protect_csrf
 @db_lookup(Protocol)
 @require_modify_right()
 def send_protocol_private(protocol):
@@ -871,6 +882,7 @@ def send_protocol_private(protocol):
 
 @app.route("/prococol/send/public/<int:protocol_id>")
 @login_required
+@protect_csrf
 @db_lookup(Protocol)
 @require_publish_right()
 def send_protocol_public(protocol):
@@ -884,6 +896,7 @@ def send_protocol_public(protocol):
 
 @app.route("/protocol/reminder/<int:protocol_id>")
 @login_required
+@protect_csrf
 @db_lookup(Protocol)
 @require_modify_right()
 def send_protocol_reminder(protocol):
@@ -947,6 +960,7 @@ def edit_top(top):
 
 @app.route("/protocol/top/delete/<int:top_id>")
 @login_required
+@protect_csrf
 @db_lookup(TOP)
 @require_modify_right()
 def delete_top(top):
@@ -961,6 +975,7 @@ def delete_top(top):
 
 @app.route("/protocol/top/move/<int:top_id>/<diff>")
 @login_required
+@protect_csrf
 @db_lookup(TOP)
 @require_modify_right()
 def move_top(top, diff):
@@ -1145,6 +1160,7 @@ def show_todo(todo):
 
 @app.route("/todo/delete/<int:todo_id>")
 @login_required
+@protect_csrf
 @db_lookup(Todo)
 @require_private_view_right()
 def delete_todo(todo):
@@ -1311,6 +1327,7 @@ def edit_document(document):
 
 @app.route("/document/delete/<int:document_id>")
 @login_required
+@protect_csrf
 @db_lookup(Document)
 @require_admin_right()
 @require_modify_right()
@@ -1325,6 +1342,7 @@ def delete_document(document):
 
 @app.route("/document/print/<int:document_id>")
 @login_required
+@protect_csrf
 @db_lookup(Document)
 @require_modify_right()
 def print_document(document):
@@ -1339,6 +1357,7 @@ def print_document(document):
 
 @app.route("/decision/print/<int:decisiondocument_id>")
 @login_required
+@protect_csrf
 @db_lookup(DecisionDocument)
 @require_modify_right()
 def print_decision(decisiondocument):
@@ -1383,6 +1402,7 @@ def show_error(error):
 
 @app.route("/error/delete/<int:error_id>")
 @login_required
+@protect_csrf
 @db_lookup(Error)
 @require_modify_right()
 def delete_error(error):
@@ -1434,6 +1454,7 @@ def edit_todomail(todomail):
 
 @app.route("/todomail/delete/<int:todomail_id>")
 @login_required
+@protect_csrf
 @db_lookup(TodoMail)
 def delete_todomail(todomail):
     name = todomail.name
@@ -1478,6 +1499,7 @@ def edit_defaultmeta(defaultmeta):
 
 @app.route("/defaultmeta/delete/<int:defaultmeta_id>")
 @login_required
+@protect_csrf
 @db_lookup(DefaultMeta)
 @require_admin_right()
 @require_modify_right()
@@ -1527,6 +1549,7 @@ def edit_decisioncategory(decisioncategory):
 
 @app.route("/decisioncategory/delete/<int:decisioncategory_id>")
 @login_required
+@protect_csrf
 @db_lookup(DecisionCategory)
 @require_admin_right()
 @require_modify_right()
@@ -1688,6 +1711,7 @@ def feed_appointments_ical(protocoltype):
 
 @app.route("/like/new")
 @login_required
+@protect_csrf
 def new_like():
     user = current_user()
     parent = None
@@ -1736,6 +1760,7 @@ def login():
 
 @app.route("/logout")
 @login_required
+@protect_csrf
 def logout():
     if "auth" in session:
         session.pop("auth")
diff --git a/templates/layout.html b/templates/layout.html
index 8cf4490de01d5598060bf10153b595631f986402..2496975f2b295f028bf7c3bc4e60ea3affb9642d 100644
--- a/templates/layout.html
+++ b/templates/layout.html
@@ -50,7 +50,7 @@
             </ul>
             <ul class="nav navbar-nav navbar-right">
                 {% if check_login() %}
-                <li><a href="{{url_for("logout")}}">Logout</a></li>
+                <li><a href="{{url_for("logout", csrf_token=get_csrf_token())}}">Logout</a></li>
                 {% else %}
                 <li><a href="{{url_for("login")}}">Login</a></li>
                 {% endif %}
diff --git a/templates/macros.html b/templates/macros.html
index 0408a6c06eaba8a19b65b4f06d895fd30d5abc94..e6d0edd7c26a03c329daaccd5abd0e6f33815956 100644
--- a/templates/macros.html
+++ b/templates/macros.html
@@ -175,7 +175,7 @@ to not render a label for the CRSFTokenField -->
             {% set verb = "likes" %}
         {% endif %}
         {% if add_link %}
-        <a href="{{url_for("new_like", next=request.url, **kwargs)}}">
+        <a href="{{url_for("new_like", csrf_token=get_csrf_token(), **kwargs)}}">
         {% endif %}
         <div class="likes-div">
             <p>{{likes|length}} <span class="like-sign">&#x1f44d;</span></p>
diff --git a/templates/protocol-show.html b/templates/protocol-show.html
index d4d99ade2f6afd87fa1affbd6d1b5fa7bc9c80fb..ba615a8260f9bb0158c176c04bbffb21a03a09c6 100644
--- a/templates/protocol-show.html
+++ b/templates/protocol-show.html
@@ -16,7 +16,7 @@
         <div class="btn-group">
             {% if has_modify_right %}
                 {% if config.ETHERPAD_ACTIVE and not protocol.public %}
-                <a class="btn {% if protocol.source is none %}btn-primary{% else %}btn-default{% endif %}" href="{{url_for("etherpull_protocol", protocol_id=protocol.id)}}">Aus Etherpad</a>
+                <a class="btn {% if protocol.source is none %}btn-primary{% else %}btn-default{% endif %}" href="{{url_for("etherpull_protocol", protocol_id=protocol.id, csrf_token=get_csrf_token())}}">Aus Etherpad</a>
                 {% endif %}
                 {% if protocol.source is not none %}
                     <a class="btn btn-primary" href="{{url_for("get_protocol_source", protocol_id=protocol.id)}}">Quelltext</a>
@@ -26,23 +26,23 @@
                 {% endif %}
                 {% if not protocol.public %}
                     {% if config.ETHERPAD_ACTIVE %}
-                    <a class="btn btn-primary" href="{{url_for("etherpush_protocol", protocol_id=protocol.id)}}"{% if large_time_diff %} onclick="return confirm('Bist du dir sicher, dass du das Template bereits in das Etherpad kopieren willst? Die Sitzung ist erst {% if time_diff.days != 1 %}in {{time_diff.days}} Tagen{% else %}morgen{% endif %}.');"{% endif %} target="_blank">Etherpad</a>
+                    <a class="btn btn-primary" href="{{url_for("etherpush_protocol", protocol_id=protocol.id, csrf_token=get_csrf_token())}}"{% if large_time_diff %} onclick="return confirm('Bist du dir sicher, dass du das Template bereits in das Etherpad kopieren willst? Die Sitzung ist erst {% if time_diff.days != 1 %}in {{time_diff.days}} Tagen{% else %}morgen{% endif %}.');"{% endif %} target="_blank">Etherpad</a>
                     {% endif %}
                 {% endif %}
                 {% if not protocol.is_done() %}
                     <a class="btn btn-default" href="{{url_for("get_protocol_template", protocol_id=protocol.id)}}">Vorlage</a>
                     {% if config.MAIL_ACTIVE %}
-                        <a class="btn btn-default" href="{{url_for("send_protocol_reminder", protocol_id=protocol.id)}}" onclick="return confirm('Bist du dir sicher, dass du manuell eine Einladung verschicken willst? Dies wird auch automatisch geschehen.');">Einladung versenden</a>
+                        <a class="btn btn-default" href="{{url_for("send_protocol_reminder", protocol_id=protocol.id, csrf_token=get_csrf_token())}}" onclick="return confirm('Bist du dir sicher, dass du manuell eine Einladung verschicken willst? Dies wird auch automatisch geschehen.');">Einladung versenden</a>
                     {% endif %}
                 {% else %}
                     {% if config.MAIL_ACTIVE %}
-                        <a class="btn btn-default" href="{{url_for("send_protocol_private", protocol_id=protocol.id)}}">Intern versenden</a>
+                        <a class="btn btn-default" href="{{url_for("send_protocol_private", protocol_id=protocol.id, csrf_token=get_csrf_token())}}">Intern versenden</a>
                         {% if protocol.public %}
-                            <a class="btn btn-default" href="{{url_for("send_protocol_public", protocol_id=protocol.id)}}">Öffentlich versenden</a>
+                            <a class="btn btn-default" href="{{url_for("send_protocol_public", protocol_id=protocol.id, csrf_token=get_csrf_token())}}">Öffentlich versenden</a>
                         {% endif %}
                     {% endif %}
                     {% if not protocol.public %}
-                        <a class="btn btn-default" href="{{url_for("publish_protocol", protocol_id=protocol.id)}}">Veröffentlichen</a>
+                        <a class="btn btn-default" href="{{url_for("publish_protocol", protocol_id=protocol.id, csrf_token=get_csrf_token())}}">Veröffentlichen</a>
                     {% endif %}
                 {% endif %}
                 <a class="btn btn-default" href="{{url_for("show_type", protocoltype_id=protocol.protocoltype.id)}}">Typ</a>
@@ -50,7 +50,7 @@
                     <a class="btn btn-success" href="{{url_for("download_document", document_id=protocol.get_compiled_document().id)}}">Download</a>
                 {% endif %}
                 {% if has_admin_right %}
-            <a class="btn btn-default" href="{{url_for("recompile_protocol", protocol_id=protocol.id)}}">Neu kompilieren</a>
+            <a class="btn btn-default" href="{{url_for("recompile_protocol", protocol_id=protocol.id, csrf_token=get_csrf_token())}}">Neu kompilieren</a>
             <a class="btn btn-danger" href="{{url_for("delete_protocol", protocol_id=protocol.id)}}" onclick="return confirm('Bist du dir sicher, dass du das Protokoll {{protocol.get_short_identifier()}} löschen möchtest?');">Löschen</a>
                 {% endif %}
             {% endif %}
@@ -104,7 +104,7 @@
                                 <li>
                                     {{decision.content}}
                                     {% if config.PRINTING_ACTIVE and has_private_view_right and decision.document is not none %}
-                                        <a href="{{url_for("print_decision", decisiondocument_id=decision.document.id)}}">Drucken</a>
+                                        <a href="{{url_for("print_decision", decisiondocument_id=decision.document.id, csrf_token=get_csrf_token())}}">Drucken</a>
                                     {% endif %}
                                     {{render_likes(decision.likes, decision_id=decision.id)}}</h2>
                                 </li>
diff --git a/templates/protocol-tops-include.html b/templates/protocol-tops-include.html
index 6be2c79f2fd726683af93a9e5132e2828f921ec4..1d9fbc21aaa11dc08148f41c011f1189fbd0e7d0 100644
--- a/templates/protocol-tops-include.html
+++ b/templates/protocol-tops-include.html
@@ -27,9 +27,9 @@
             {% endif %}
             {% if not protocol.is_done() and has_modify_right %}
                 <a href="{{url_for('edit_top', top_id=top.id)}}">Ändern</a>
-                <a href="{{url_for('move_top', top_id=top.id, diff=1)}}">Runter</a>
-                <a href="{{url_for('move_top', top_id=top.id, diff=-1)}}">Hoch</a>
-                <a href="{{url_for('delete_top', top_id=top.id)}}" onclick="return confirm('Bist du dir sicher, dass du den TOP {{top.name}} löschen möchtest?');">Löschen</a>
+                <a href="{{url_for('move_top', top_id=top.id, diff=1, csrf_token=get_csrf_token())}}">Runter</a>
+                <a href="{{url_for('move_top', top_id=top.id, diff=-1, csrf_token=get_csrf_token())}}">Hoch</a>
+                <a href="{{url_for('delete_top', top_id=top.id, csrf_token=get_csrf_token())}}" onclick="return confirm('Bist du dir sicher, dass du den TOP {{top.name}} löschen möchtest?');">Löschen</a>
             {% endif %}
             {% if has_private_view_right and top.description is not none and top.description|length > 0 %}
                 <span class="glyphicon glyphicon-info-sign"></span>
diff --git a/utils.py b/utils.py
index 1c93a53b051cd8fd875be4931d5f125a79eaab83..5e80b9cbebc40ae8cacf6cff9201fa12c1577e17 100644
--- a/utils.py
+++ b/utils.py
@@ -1,4 +1,4 @@
-from flask import request
+from flask import request, session
 
 import random
 import string
@@ -14,6 +14,8 @@ import ipaddress
 from socket import getfqdn
 from uuid import uuid4
 import subprocess
+import os
+import hashlib
 
 import config
 
@@ -258,3 +260,9 @@ def get_max_page_length_exp(objects):
 
 def get_internal_filename(protocol, document, filename):
     return "{}-{}-{}".format(protocol.id, document.id, filename)
+
+
+def get_csrf_token():
+    if "_csrf" not in session:
+        session["_csrf"] = hashlib.sha1(os.urandom(64)).hexdigest()
+    return session["_csrf"]
diff --git a/views/tables.py b/views/tables.py
index 9093663017a4ff3098e28f9d8d2d2349f5229e7c..c30216819d342c0f93d00607b7569a8816cd5b11 100644
--- a/views/tables.py
+++ b/views/tables.py
@@ -1,5 +1,6 @@
 from flask import Markup, url_for, request
 from shared import date_filter, datetime_filter, time_filter, current_user
+from utils import get_csrf_token
 
 import config
 
@@ -341,7 +342,8 @@ class ProtocolTypeTable(SingleValueTable):
             ]))]
         action_part = [
             Table.link(
-                url_for("delete_type", protocoltype_id=self.value.id),
+                url_for("delete_type", protocoltype_id=self.value.id,
+                        csrf_token=get_csrf_token()),
                 "Löschen",
                 confirm="Bist du dir sicher, dass du den Protokolltype "
                         "{} löschen möchtest?".format(self.value.name))
@@ -371,10 +373,12 @@ class DefaultTOPsTable(Table):
             top.number,
             Table.concat([
                 Table.link(
-                    url_for("move_default_top", defaulttop_id=top.id, diff=1),
+                    url_for("move_default_top", defaulttop_id=top.id, diff=1,
+                            csrf_token=get_csrf_token()),
                     "Runter"),
                 Table.link(
-                    url_for("move_default_top", defaulttop_id=top.id, diff=-1),
+                    url_for("move_default_top", defaulttop_id=top.id, diff=-1,
+                            csrf_token=get_csrf_token()),
                     "Hoch"),
                 Table.link(
                     url_for(
@@ -383,7 +387,8 @@ class DefaultTOPsTable(Table):
                         defaulttop_id=top.id),
                     "Ändern"),
                 Table.link(
-                    url_for("delete_default_top", defaulttop_id=top.id),
+                    url_for("delete_default_top", defaulttop_id=top.id,
+                            csrf_token=get_csrf_token()),
                     "Löschen",
                     confirm="Bist du dir sicher, dass du den Standard-TOP "
                             "{} löschen willst?".format(top.name))
@@ -413,7 +418,8 @@ class MeetingRemindersTable(Table):
                 url_for("edit_reminder", meetingreminder_id=reminder.id),
                 "Ändern"),
             Table.link(
-                url_for("delete_reminder", meetingreminder_id=reminder.id),
+                url_for("delete_reminder", meetingreminder_id=reminder.id,
+                        csrf_token=get_csrf_token()),
                 "Löschen",
                 confirm="Bist du dir sicher, dass du die Einladungsmail {} "
                         "Tage vor der Sitzung löschen willst?".format(
@@ -452,7 +458,8 @@ class ErrorsTable(Table):
             datetime_filter(error.datetime),
             error.get_short_description(),
             Table.link(
-                url_for("delete_error", error_id=error.id, next=request.path),
+                url_for("delete_error", error_id=error.id,
+                        csrf_token=get_csrf_token()),
                 "Löschen",
                 confirm="Bist du dir sicher, dass du den Fehler löschen "
                         "möchtest?")
@@ -519,7 +526,8 @@ class TodosTable(Table):
         if todo.protocoltype.has_modify_right(user):
             row.append(Table.concat([
                 Table.link(url_for("edit_todo", todo_id=todo.id), "Ändern"),
-                Table.link(url_for("delete_todo", todo_id=todo.id), "Löschen")
+                Table.link(url_for("delete_todo", todo_id=todo.id,
+                           csrf_token=get_csrf_token()), "Löschen")
             ]))
         else:
             row.append("")
@@ -552,7 +560,8 @@ class TodoTable(SingleValueTable):
                 Table.link(
                     url_for("edit_todo", todo_id=self.value.id), "Ändern"),
                 Table.link(
-                    url_for("delete_todo", todo_id=self.value.id), "Löschen",
+                    url_for("delete_todo", todo_id=self.value.id,
+                            csrf_token=get_csrf_token()), "Löschen",
                     confirm="Bist du dir sicher, dass du das Todo löschen "
                             "willst?")
             ]))
@@ -592,7 +601,8 @@ class DecisionsTable(Table):
             Table.link(
                 url_for(
                     "print_decision",
-                    decisiondocument_id=decision.document.id),
+                    decisiondocument_id=decision.document.id,
+                    csrf_token=get_csrf_token()),
                 "Drucken")
             if (config.PRINTING_ACTIVE
                 and decision.protocol.protocoltype.has_modify_right(user)
@@ -634,11 +644,13 @@ class DocumentsTable(Table):
                 "Bearbeiten"))
         if config.PRINTING_ACTIVE and document.protocol.has_modify_right(user):
             links.append(Table.link(
-                url_for("print_document", document_id=document.id),
+                url_for("print_document", document_id=document.id,
+                        csrf_token=get_csrf_token()),
                 "Drucken"))
         if document.protocol.protocoltype.has_admin_right(user):
             links.append(Table.link(
-                url_for("delete_document", document_id=document.id),
+                url_for("delete_document", document_id=document.id,
+                        csrf_token=get_csrf_token()),
                 "Löschen",
                 confirm="Bist du dir sicher, dass du das Dokument {} löschen "
                         "willst?".format(document.name)))
@@ -675,7 +687,8 @@ class TodoMailsTable(Table):
                     url_for("edit_todomail", todomail_id=todomail.id),
                     "Ändern"),
                 Table.link(
-                    url_for("delete_todomail", todomail_id=todomail.id),
+                    url_for("delete_todomail", todomail_id=todomail.id,
+                            csrf_token=get_csrf_token()),
                     "Löschen",
                     confirm="Bist du dir sicher, dass du die "
                             "Todomailzuordnung {} zu {} löschen "
@@ -707,7 +720,8 @@ class DefaultMetasTable(Table):
             Table.link(
                 url_for("edit_defaultmeta", defaultmeta_id=meta.id), "Ändern"),
             Table.link(
-                url_for("delete_defaultmeta", defaultmeta_id=meta.id),
+                url_for("delete_defaultmeta", defaultmeta_id=meta.id,
+                        csrf_token=get_csrf_token()),
                 "Löschen",
                 confirm="Bist du dir sicher, dass du das Metadatenfeld {} "
                         "löschen willst?".format(meta.name))
@@ -739,7 +753,8 @@ class DecisionCategoriesTable(Table):
                 Table.link(
                     url_for(
                         "delete_decisioncategory",
-                        decisioncategory_id=category.id),
+                        decisioncategory_id=category.id,
+                        csrf_token=get_csrf_token()),
                     "Löschen",
                     confirm="Bist du dir sicher, dass du die "
                             "Beschlusskategorie {} löschen "