login POST now requires the X-Aniweb header: cross-site forced-login (attacker-form session swap) dies at the 403; login.html sends the header like the SPA
This commit is contained in:
1 parent
3e5c7062ab
commit
882263d276
2 files changed
+27
-7
No files matched your search
+6
-4
@@ -146,10 +146,12 @@ def _session_user(request) -> str | None:
|
||||
async def csrf_header(request, call_next):
|
||||
# ponytail: a cross-site form POST (top-level navigation) does carry the
|
||||
# SameSite=Lax cookie, so <form action=.../account/delete> from a
|
||||
# malicious page would work; forms can't set a custom header, the SPA
|
||||
# always sends it. Runs *after* require_auth (registered earlier), so
|
||||
# unauthenticated requests still get 302/401 first.
|
||||
if request.method in ("POST", "DELETE") and request.url.path != "/login":
|
||||
# malicious page would work; forms can't set a custom header, the SPA and
|
||||
# the login page both send one. /login is covered too: login needs no
|
||||
# cookie, so an exempt /login let a cross-site forced-login POST swap a
|
||||
# visitor's session onto the attacker's account. Runs *after* require_auth
|
||||
# (registered earlier), so unauthenticated GETs still 302/401 first.
|
||||
if request.method in ("POST", "DELETE"):
|
||||
if request.headers.get("x-aniweb") != "1":
|
||||
return JSONResponse({"detail": "forbidden"}, status_code=403)
|
||||
return await call_next(request)
|
||||
|
||||
Reference in new issue
Block a user