CP_EHRENAMT/app/csrf.py
2026-06-25 18:58:44 +02:00

50 lines
1.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""CSRF-Schutz leichtgewichtig, ohne Flask-WTF (siehe CLAUDE.md: „so wenig wie möglich").
Ein Session-gebundenes Token wird in jedes POST-Formular geschrieben und bei jeder
unsicheren Anfrage geprüft. Reicht für eine interne App mit Session-Cookies.
Nutzung im Template:
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
"""
import hmac
import secrets
from flask import abort, render_template, request, session
CSRF_SESSION_KEY = "_csrf_token"
CSRF_FORM_FIELD = "csrf_token"
CSRF_HEADER = "X-CSRFToken"
SAFE_METHODS = {"GET", "HEAD", "OPTIONS", "TRACE"}
def generate_csrf():
"""Token der aktuellen Session zurückgeben (beim ersten Aufruf erzeugen)."""
if CSRF_SESSION_KEY not in session:
session[CSRF_SESSION_KEY] = secrets.token_urlsafe(32)
return session[CSRF_SESSION_KEY]
def _validate_csrf():
erwartet = session.get(CSRF_SESSION_KEY)
gesendet = request.form.get(CSRF_FORM_FIELD) or request.headers.get(CSRF_HEADER)
# compare_digest: konstante Laufzeit, verträgt aber keine None-Werte.
if not erwartet or not gesendet or not hmac.compare_digest(erwartet, gesendet):
abort(400, description="csrf")
def init_csrf(app):
@app.before_request
def csrf_protect():
if request.method not in SAFE_METHODS:
_validate_csrf()
@app.errorhandler(400)
def csrf_fehler(err):
# Nur die CSRF-Fälle bekommen die freundliche Seite; andere 400 unverändert.
if getattr(err, "description", None) == "csrf":
return render_template("400_csrf.html"), 400
return err
# Im Template als csrf_token() verfügbar machen.
app.jinja_env.globals["csrf_token"] = generate_csrf