EXERCISE 2 — Diagnosing your admin-login "Invalid CSRF token" error ==================================================================== THE GENERAL MECHANISM: - The login page is rendered with a hidden CSRF token bound to your CURRENT (pre-login) session. - The server validates a submitted token against the token currently stored for your session. - If, by the time you submit, the stored/expected token no longer matches the one embedded in the form you're submitting, you get "Invalid CSRF token." (HTTP 403.) - Clicking BACK reloads a login form whose embedded token matches the CURRENT session state, so the next submit validates and succeeds. THE THREE LIKELY ROOT CAUSES (any one, or a combination): 1. STALE CACHED / BFCACHED FORM. The browser served the login form from cache -- specifically Firefox's back/forward cache (bfcache), which restores a fully-live previous page from memory. That restored page carries an OLD embedded token. The server has since moved on (new token/session), so the old token fails. -> This is why you see it specifically in FIREFOX: its bfcache is aggressive and will re-show a login page (and its stale token) that other browsers might have re-fetched fresh. 2. SESSION REGENERATION ON LOGIN. Good practice is to REGENERATE the session id on successful login to prevent SESSION FIXATION. If the CSRF token is tied to the session and the session is rotated during the login process, the token the form was rendered with no longer matches the new session -> mismatch. (Also happens if an earlier login already rotated things and you resubmit an old page.) 3. PER-REQUEST TOKEN ROTATION / MULTIPLE TABS. If the app issues a NEW token on every request (per-request pattern), then loading the login page in one tab, then doing anything that rotates the token (another tab, an earlier load), leaves the first tab's form holding a token the server already replaced. WHY BACK "FIXES" IT: - Hitting Back causes the browser to present a login form again; in the failing-then-working pattern, the reloaded/re-fetched form now carries a token consistent with the current session, so submitting it passes validation. You're effectively re-syncing the token to the session. IS IT A SECURITY PROBLEM? No -- it's the defence working, just firing on a STALE (not malicious) token. A real CSRF attacker never had a valid token at all. The annoyance is a USABILITY bug, not a vulnerability. PROPER FIXES (expanded in Chapter 10): - Send Cache-Control: no-store on the login page so the form (and its token) isn't served stale from cache/bfcache. - Issue/refresh the CSRF token AFTER any session regeneration, so the rendered form matches the post-regeneration session. - Prefer a per-session token (or a token that tolerates refresh) for login forms rather than aggressive per-request rotation. - Optionally, on token mismatch for a LOGIN specifically, re-render the login form with a fresh token instead of a hard error.