security: Secure cookie flag (ANIWEB_SECURE, on in run.sh) + kill sessions of deleted accounts
- middleware re-verifies the username against users.json per request: deleted accounts' signed cookies are rejected immediately - set_cookie/delete_cookie honor the Secure flag; run.sh sets ANIWEB_SECURE=1 so sessions only work through the HTTPS proxy
This commit is contained in:
1 parent
7e6db5a41c
commit
cdb25b1aba
4 files changed
+20
-5
No files matched your search
+8
-3
@@ -27,6 +27,7 @@ _SIGNER = TimestampSigner(users.get_secret())
|
||||
_SESSION = "aniweb_session"
|
||||
_SESSION_AGE = 30 * 86400
|
||||
_PUBLIC = {("/login", "GET"), ("/login", "POST")}
|
||||
_SECURE = os.environ.get("ANIWEB_SECURE") == "1" # set when served over HTTPS
|
||||
|
||||
# ponytail: in-memory lockout per username, single process — 5 fails -> 15 min
|
||||
_lockout: dict = {}
|
||||
@@ -59,6 +60,10 @@ async def require_auth(request, call_next):
|
||||
if (request.url.path, request.method) in _PUBLIC:
|
||||
return await call_next(request)
|
||||
user = _session_user(request)
|
||||
# stateless cookie -> re-verify the account still exists, so
|
||||
# deleted users' cookies die immediately
|
||||
if user is not None and user not in users.load():
|
||||
user = None
|
||||
if not user:
|
||||
if request.url.path.startswith("/api/"):
|
||||
return JSONResponse({"detail": "not authenticated"}, status_code=401)
|
||||
@@ -70,7 +75,7 @@ async def require_auth(request, call_next):
|
||||
|
||||
def _session_cookie(resp, user: str):
|
||||
resp.set_cookie(_SESSION, _SIGNER.sign(b"user:" + user.encode()).decode(),
|
||||
max_age=_SESSION_AGE, httponly=True, samesite="lax")
|
||||
max_age=_SESSION_AGE, httponly=True, samesite="lax", secure=_SECURE)
|
||||
|
||||
|
||||
@app.get("/login", include_in_schema=False)
|
||||
@@ -102,14 +107,14 @@ def api_me(request: Request):
|
||||
def delete_account(request: Request):
|
||||
users.remove(request.state.user)
|
||||
resp = JSONResponse({"ok": True})
|
||||
resp.delete_cookie(_SESSION)
|
||||
resp.delete_cookie(_SESSION, secure=_SECURE)
|
||||
return resp
|
||||
|
||||
|
||||
@app.post("/logout")
|
||||
def logout():
|
||||
resp = JSONResponse({"ok": True})
|
||||
resp.delete_cookie(_SESSION)
|
||||
resp.delete_cookie(_SESSION, secure=_SECURE)
|
||||
return resp
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user