CSRF Double Submit Cookie Pattern
debt(d7/e5/b5/t7)
Closest to 'only careful code review or runtime testing' (d7). Semgrep is listed in detection_hints.tools but automated detection is marked 'no'. Missing CSRF token checks or weak implementations (predictable tokens, missing SameSite) require manual security review or penetration testing to identify. No standard linter catches the subtle flaws like subdomain cookie attacks.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix recommends switching to synchroniser token pattern with session storage, which means adding session infrastructure, modifying all form handlers to generate/embed tokens, and updating validation logic across state-changing endpoints (POST/PUT/DELETE). Not a one-line fix, but contained to the auth/form handling layer.
Closest to 'persistent productivity tax' (b5). Applies only to web contexts, but CSRF protection is cross-cutting across all form handlers and state-changing endpoints. Once chosen, the double-submit pattern shapes how every form and AJAX request is built. Switching to synchroniser tokens later requires touching every protected endpoint. The architectural commitment affects multiple work streams but doesn't define the entire system.
Closest to 'serious trap' (t7). The misconception explicitly states developers believe double-submit is always safe, but subdomain takeover or XSS allows attackers to forge matching token pairs. This contradicts how developers expect stateless CSRF protection to work — they assume cookie matching is sufficient when it has a fundamental bypass. The common_mistakes reinforce multiple non-obvious failure modes.
Also Known As
TL;DR
Explanation
The double-submit cookie pattern is a stateless alternative to synchronised token pattern for CSRF protection. A cryptographically random value is set as a non-HttpOnly cookie; the client reads it via JavaScript and includes it in request headers or body parameters. The server verifies both values match. Because cross-origin requests cannot read cookies, an attacker cannot forge the matching pair. This pattern is suitable for SPAs but is undermined by subdomains that can set cookies on the parent domain — use the Signed Double-Submit Cookie variant to counter this.
Common Misconception
Why It Matters
Common Mistakes
- Not using a cryptographically random token — a predictable cookie value is forgeable.
- Subdomain cookie attacks: if attacker can write cookies for a subdomain, they can forge the double-submit.
- Not using SameSite=Strict or Lax alongside double-submit — they are complementary defences.
- Validating the token only on POST and not on other state-changing methods like PUT and DELETE.
Code Examples
// Predictable CSRF token — forgeable:
setcookie('csrf_token', md5(session_id())); // Predictable — session ID may be leaked
// Correct — random token:
$token = bin2hex(random_bytes(32));
setcookie('csrf_token', $token, ['samesite' => 'Strict', 'secure' => true]);
// Verify: $_POST['csrf_token'] === $_COOKIE['csrf_token']
// Double Submit Cookie — for stateless APIs that can't use server sessions
// 1. Server sets non-HttpOnly cookie (JS must read it)
setcookie('csrf_token', \$token = bin2hex(random_bytes(32)), [
'path' => '/',
'secure' => true,
'httponly' => false, // JS must be able to read this
'samesite' => 'Strict',
]);
// 2. JS sends cookie value as a custom request header:
// fetch('/api/orders', {
// method: 'POST',
// headers: {'X-CSRF-Token': getCookie('csrf_token')},
// });
// 3. Server verifies header matches cookie:
\$cookie = \$_COOKIE['csrf_token'] ?? '';
\$header = \$_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
if (!hash_equals(\$cookie, \$header)) abort(403);
// Cross-site requests cannot read the cookie — so header can't be forged