EXERCISE 1 — A basic double-submit cookie check in Express =========================================================== const crypto = require("crypto"); const express = require("express"); const cookieParser = require("cookie-parser"); const app = express(); app.use(cookieParser()); app.use(express.json()); // Issue a JS-READABLE token cookie (note: NOT httpOnly, so the client // script can read it to echo into the header). app.use((req, res, next) => { if (!req.cookies.csrf) { const token = crypto.randomBytes(32).toString("hex"); res.cookie("csrf", token, { sameSite: "lax", secure: true }); req.cookies.csrf = token; // so it's usable this request too } next(); }); // Validate: the X-CSRF-Token header must equal the csrf cookie. function checkCsrf(req, res, next) { const cookie = req.cookies.csrf || ""; const header = req.get("X-CSRF-Token") || ""; const a = Buffer.from(cookie), b = Buffer.from(header); const ok = a.length === b.length && crypto.timingSafeEqual(a, b); if (!ok) return res.status(403).send("Invalid CSRF token."); next(); } app.post("/transfer", checkCsrf, (req, res) => res.send("Transfer processed.")); // Client side (same-origin page): // fetch("/transfer", { // method: "POST", // headers: { "X-CSRF-Token": readCookie("csrf") }, // credentials: "include" // }); WHY A CROSS-SITE FORGED REQUEST FAILS THIS CHECK: - On a forged request from evil.com, the browser DOES automatically attach the csrf COOKIE (ambient authority) -- so req.cookies.csrf is present. - BUT the attacker's page cannot READ that cookie's value: it belongs to your origin, and the Same-Origin Policy blocks evil.com's script from reading it. So the attacker has no way to learn what to put in the X-CSRF-Token header. - Worse for the attacker: setting a custom header on a cross-origin request forces a CORS PREFLIGHT they can't pass. So they can't set the header at all on a simple cross-site request. - Result: the forged request arrives with the cookie but NO (or wrong) header. cookie !== header -> 403. The defence holds. - The whole thing rests on the asymmetry: the cookie is auto-SENT, but only same-origin JS can READ it to populate the matching header. CAVEAT: this NAIVE version (bare value, cookie==header) is vulnerable to cookie injection from a subdomain (Exercise 2). Prefer the signed/HMAC variant or a maintained library in production.