fix(auth): [operator-action] hash session tokens at rest #496
No reviewers
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
psa-systems/lets-chat!496
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/LC-514-sessions-hash-at-rest"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
LC-514:
sessions.idheld the RAW session cookie value (a 64-char OsRng alphanumeric token). A read-only DB compromise (backup leak, replica with read access, ops-shell SELECT) handed the attacker live session cookies that could be pasted straight into thesession=cookie header to impersonate users without ever cracking a password. The audit named this high-severity.The cookie value itself is a 256-bit opaque random token; storing the plaintext as the lookup key treats the DB as a secret-keeping vault, which it isn't. Now: the cookie still carries the plaintext, but every lookup hashes the presented value via SHA-256 before the WHERE clause, and the DB only ever stores hashes.
Pieces:
db::auth::hash_session_token(token) -> String(SHA-256 hex, idempotent on the token-shape).create_session_with_originwritesSHA-256(token)assessions.id; the plaintext is returned to the caller for the cookie set as before.get_user_by_session,touch_session_last_seen, and the twodelete_session*paths hash the presented cookie before the WHERE clause.routes/settings.rs::post_session_revokeand theis_currentmarker in the sessions list both hash the cookie value before comparing against the row id (which is now the hash).lookup_session_idis the per-call helper that accepts either a raw cookie (hashes it) or an already-hashed row id (passes through). The settings UI's "Sign out this session" button posts the listed id (the hash) and that still works.backfill_sessions_hashed_at_restruns once at startup after migration 0034. Existing rows have theiridupdated in place toSHA-256(id)so the lookup against the new hashed key matches the cookies that are already in flight; no user is forced through a re-login. Idempotent (gated by thesessions_hash_migration_markertable, AND skips rows whose id is already SHA-256 hex), and survives crashes mid-backfill since each UPDATE is its own transaction.Operator-Action: the next startup after this deploy runs a one-shot in-place re-hash of every existing
sessionsrow. Existing user sessions stay valid because the cookie value is the input to the new hashed lookup. A backup taken BEFORE the deploy still leaks the plaintext cookies it captured; rotate / invalidate those backups after the deploy if their cookie window has not expired (default cookie TTL is 30 days).#LC-514