security: harden JWT auth with session binding, CSP headers, and sessionStorage

- Reduce access_token_ttl from 900s to 300s (5 min) to shrink XSS exploit window
- Add session_id claim to JWT tokens tied to browser session (X-Session-Id header)
- Flask middleware validates session_id matches header on every request
- CSP headers: default-src/script-src 'self', no unsafe-inline/eval, frame-ancestors none
- X-Content-Type-Options: nosniff on all responses
- Move refresh token from localStorage to sessionStorage (tab-scoped, cleared on close)
- Timing-safe password verification (dummy Argon2id for unknown users)
- WebSocket auth also validates session_id header
- Add 5 session_id tests and 3 CSP header tests
This commit is contained in:
2026-07-24 02:51:54 +00:00
parent 56b200d233
commit a365059976
13 changed files with 317 additions and 96 deletions
+4 -1
View File
@@ -52,6 +52,8 @@ Q_SELECT_WEBAUTHN_COUNTS = "select_webauthn_counts"
Q_DELETE_WEBAUTHN = "delete_webauthn"
Q_UPDATE_WEBAUTHN_SIGN_COUNT = "update_webauthn_sign_count"
Q_SELECT_ALL_USERS = "select_all_users"
Q_SELECT_USER_JWT_SECRET = "select_user_jwt_secret"
Q_UPDATE_JWT_SECRET = "update_jwt_secret"
# ---------------------------------------------------------------------------
# Schema DDL
@@ -62,8 +64,9 @@ CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
jwt_secret TEXT NOT NULL,
created_at INTEGER NOT NULL DEFAULT (unixepoch())
);
);
CREATE TABLE IF NOT EXISTS permissions (
id INTEGER PRIMARY KEY AUTOINCREMENT,