From e062a1e83606e7a49996b3f2dbc1b31b9241dc64 Mon Sep 17 00:00:00 2001 From: Bjoern Welker Date: Fri, 6 Feb 2026 08:01:22 +0100 Subject: [PATCH] security: add CSRF protection to all forms - Add Flask-WTF dependency for CSRF protection - Initialize CSRFProtect in app.py - Add CSRF tokens to all POST forms in templates - Exempt /order JSON API endpoint (uses API key instead) This protects against Cross-Site Request Forgery attacks on all admin and user management operations. Co-Authored-By: Claude Sonnet 4.5 --- requirements.txt | 4 ++++ wawi/app.py | 6 ++++++ wawi/templates/ausbuchen.html | 1 + wawi/templates/edit.html | 1 + wawi/templates/index.html | 2 ++ wawi/templates/login.html | 1 + wawi/templates/orders.html | 2 ++ wawi/templates/users.html | 3 +++ 8 files changed, 20 insertions(+) create mode 100755 requirements.txt mode change 100644 => 100755 wawi/templates/ausbuchen.html mode change 100644 => 100755 wawi/templates/edit.html mode change 100644 => 100755 wawi/templates/index.html mode change 100644 => 100755 wawi/templates/login.html mode change 100644 => 100755 wawi/templates/orders.html mode change 100644 => 100755 wawi/templates/users.html diff --git a/requirements.txt b/requirements.txt new file mode 100755 index 0000000..2d392cf --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +Flask>=3.0.0 +Flask-WTF>=1.2.0 +Werkzeug>=3.0.0 +gunicorn>=21.0.0 diff --git a/wawi/app.py b/wawi/app.py index 45911f1..3da2967 100755 --- a/wawi/app.py +++ b/wawi/app.py @@ -22,6 +22,7 @@ from datetime import datetime from typing import Any from flask import Flask, Blueprint, g, flash, jsonify, redirect, render_template, request, session, url_for +from flask_wtf.csrf import CSRFProtect from werkzeug.security import check_password_hash, generate_password_hash from werkzeug.utils import secure_filename @@ -41,6 +42,10 @@ app.config["SESSION_COOKIE_SAMESITE"] = "Lax" app.config["SESSION_COOKIE_SECURE"] = os.environ.get("COOKIE_SECURE", "1") == "1" app.config["SESSION_COOKIE_HTTPONLY"] = True app.config["MAX_CONTENT_LENGTH"] = 5 * 1024 * 1024 + +# CSRF-Schutz aktivieren +csrf = CSRFProtect(app) + bp = Blueprint("bp", __name__) UPLOAD_DIR.mkdir(parents=True, exist_ok=True) @@ -597,6 +602,7 @@ def build_bestand() -> list[dict]: @bp.route("/order", methods=["POST"]) +@csrf.exempt # JSON API ohne CSRF-Schutz (nutzt API-Key stattdessen) def order(): """Erstellt eine Bestellung (optional API‑Key) und versendet Mail.""" ip = request.headers.get("X-Forwarded-For", request.remote_addr or "unknown").split(",")[0].strip() diff --git a/wawi/templates/ausbuchen.html b/wawi/templates/ausbuchen.html old mode 100644 new mode 100755 index f941ce1..04adf9c --- a/wawi/templates/ausbuchen.html +++ b/wawi/templates/ausbuchen.html @@ -4,6 +4,7 @@

Ausbuchen: {{ item.artikel }} ({{ item.groesse }})

Aktueller Bestand: {{ item.gezaehlt }}
+