EXERCISE 3 — Three flawed synchronizer-token implementations ============================================================= (a) Token stored only in a SECOND cookie; server checks the two cookies match. HOW THE ATTACKER DEFEATS IT: - BOTH cookies (the session cookie AND the token cookie) are sent AUTOMATICALLY by the browser on a forged cross-site request (ambient authority). The attacker doesn't need to know either value -- the browser attaches them. - The server sees two cookies that match (they always match, they're both just sent as-is) and accepts the forged request. No protection. - NOTE: this is NOT the synchronizer pattern; it's a broken cousin. (The legitimate "double-submit" pattern in Chapter 6 requires the token to be echoed in a BODY/HEADER and compared to the cookie -- the header copy is what the attacker can't set. Comparing two COOKIES provides nothing.) FIX: send the token in the request BODY or a custom HEADER and compare THAT against the session-stored token (true synchronizer) or against the cookie value (true double-submit). The non-cookie copy is the whole point. (b) Only POST routes validate the token, but GET /delete?id= exists. HOW THE ATTACKER DEFEATS IT: - The state-changing GET requires no token, so a single on any page the victim loads fires a credentialed GET and deletes the record. The POST protection is irrelevant -- the attacker just uses the unprotected GET. FIX: (1) never change state on GET -- make destructive actions POST (or PUT/DELETE) and (2) validate the CSRF token on EVERY state-changing endpoint, not just POST. GET must be safe/side-effect-free. (c) Token generated with Math.random(). HOW THE ATTACKER DEFEATS IT: - Math.random() is NOT cryptographically secure: its output is predictable and its internal state can be recovered from a few observed values. An attacker can predict/derive the token a victim will have, embed that value in a forged request, and pass validation -- defeating the defence without ever reading the page. FIX: generate tokens with a CSPRNG, e.g. crypto.randomBytes(32) (Node) / a secure random API, with enough entropy (>= 128 bits). Also compare in constant time (crypto.timingSafeEqual) to avoid timing leaks. GENERAL LESSON: The synchronizer pattern is robust, but it only protects what it COVERS and only if the token is (i) unpredictable, (ii) carried outside cookies, and (iii) validated on every state-changing route. Each flaw above breaks one of those three conditions.