"""CSRF protection (session-scoped token, no external dependency).""" import hmac import logging import secrets from flask import abort, request, session logger = logging.getLogger(__name__) def get_csrf_token(): """Return the per-session CSRF token, generating one on first use.""" token = session.get("_csrf_token") if not token: token = secrets.token_hex(32) session["_csrf_token"] = token return token def validate_csrf(): """Abort 403 if the form CSRF token doesn't match the session token.""" expected = session.get("_csrf_token") provided = request.form.get("csrf_token", "") if not expected or not provided or not hmac.compare_digest(expected, provided): logger.warning( "CSRF validation failed for %s from %s", request.path, request.remote_addr, ) abort(403) def init_app(app): """Expose csrf_token() to Jinja templates as a global.""" app.jinja_env.globals["csrf_token"] = get_csrf_token