From 843eda6fea81300c17cb0540d1a6ecd60bf8ff58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=89=E9=91=AB=E5=8D=87?= Date: Thu, 21 May 2026 17:45:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=85=AC=E5=BC=80=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=B1=81=E5=85=8D=20CSRF=20=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /api/public/ 命名空间下的匿名 PUT/POST 请求没有会话 cookie, 被 CSRF 中间件拦在 403。auth 中间件已放行该前缀,CSRF 这里补齐豁免, 让公开定时任务页的编辑保存能正常调用。 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../backend/app/gateway/csrf_middleware.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/offline-backend-20260512/backend/app/gateway/csrf_middleware.py b/offline-backend-20260512/backend/app/gateway/csrf_middleware.py index a4269f55..b471e207 100644 --- a/offline-backend-20260512/backend/app/gateway/csrf_middleware.py +++ b/offline-backend-20260512/backend/app/gateway/csrf_middleware.py @@ -37,9 +37,17 @@ def should_check_csrf(request: Request) -> bool: if request.method not in ("POST", "PUT", "DELETE", "PATCH"): return False - path = request.url.path.rstrip("/") + path = request.url.path + stripped = path.rstrip("/") # Exempt /api/v1/auth/me endpoint - if path == "/api/v1/auth/me": + if stripped == "/api/v1/auth/me": + return False + # Anonymous public endpoints (mirror auth_middleware._PUBLIC_PATH_PREFIXES). + # These intentionally have no cookie session, so the Double-Submit Cookie + # pattern is both impossible (no token to compare) and unnecessary (no + # session for an attacker to ride). Without this exemption every public + # PUT/POST returns 403 even though auth was bypassed earlier in the chain. + if path.startswith("/api/public/"): return False return True