HTTP cookies remain the foundation of web authentication and session management. However, because browsers automatically attach cookies to outbound HTTP requests, cookies are inherently susceptible to Cross-Site Request Forgery (CSRF), session fixation, and cookie tossing attacks across untrusted subdomains.
Recent browser security evolutions—including the standard default of SameSite=Lax, strict RFC 6265bis Cookie Name Prefixes (__Host- and __Secure-), and CHIPS (Cookies Having Independent Partitioned State)—have revolutionized cookie defense.
This guide provides a comprehensive security checklist for modern cookie configuration, breaks down SameSite behaviors, and details how to implement zero-trust session storage in production applications.
1. The Modern Set-Cookie Header Directive Matrix
Every authentication or session cookie sent from a production server should leverage the full suite of protective flags:
Set-Cookie: __Host-session_id=s%3A7f9b8c2d; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=604800
┌─────────────────┬───────────────────────────────────────────────────────────┐
│ Directive │ Security Purpose │
├─────────────────┼───────────────────────────────────────────────────────────┤
│ `Secure` │ Restricts cookie transmission exclusively to HTTPS. │
│ │ Prevents plaintext leakage over unencrypted networks. │
├─────────────────┼───────────────────────────────────────────────────────────┤
│ `HttpOnly` │ Prevents client-side scripts from accessing `document. │
│ │ cookie`. Fully mitigates credential theft via XSS. │
├─────────────────┼───────────────────────────────────────────────────────────┤
│ `SameSite=Lax` │ Blocks cookies on cross-origin subrequests (AJAX, images, │
│ │ iframes) while permitting top-level GET navigations. │
├─────────────────┼───────────────────────────────────────────────────────────┤
│ `SameSite=Strict│ Blocks cookies on ALL cross-origin requests, including │
│ │ direct external link clicks. │
├─────────────────┼───────────────────────────────────────────────────────────┤
│ `__Host-` prefix│ Forces `Secure`, `Path=/`, and forbids domain delegation │
│ │ to subdomains. Prevents subdomain cookie injection. │
├─────────────────┼───────────────────────────────────────────────────────────┤
│ `Partitioned` │ Enables CHIPS: isolates third-party cookies per top-level │
│ │ site partition, preventing cross-site tracking. │
└─────────────────┴───────────────────────────────────────────────────────────┘
2. SameSite: Lax vs. Strict vs. None
Understanding the exact request conditions under which cookies are sent is critical for both security and user experience:
┌──────────────────────────────────────────────┬──────────┬──────────┬──────────┐
│ Request Scenario │ Strict │ Lax │ None │
├──────────────────────────────────────────────┼──────────┼──────────┼──────────┤
│ User clicks top-level link from external site│ ❌ No │ ✅ Yes │ ✅ Yes │
│ External site triggers POST form submit │ ❌ No │ ❌ No │ ✅ Yes │
│ Cross-origin `fetch()` / AJAX request │ ❌ No │ ❌ No │ ✅ Yes │
│ Cross-origin `<iframe>`, `<img>`, `<script>` │ ❌ No │ ❌ No │ ✅ Yes │
│ Navigating within the same origin (same site)│ ✅ Yes │ ✅ Yes │ ✅ Yes │
└──────────────────────────────────────────────┴──────────┴──────────┴──────────┘
The SameSite=Lax 2-Minute Window Gotcha ("Lax-allowing-unsafe")
In Chromium-based browsers, cookies without an explicit SameSite attribute default to SameSite=Lax. However, to avoid breaking legacy authentication flows, Chromium applies a 2-minute grace period: top-level cross-site POST requests sent within 120 seconds of cookie creation will still attach the cookie.
Production Rule: Never rely on browser defaults. Always set SameSite=Lax or SameSite=Strict explicitly in your server headers.
3. Hardening with Cookie Name Prefixes (__Host- & __Secure-)
Under standard cookie rules, any sub-domain (e.g. vulnerable.api.example.com or compromised marketing.example.com) can set a cookie for the parent domain .example.com and overwrite or poison the session of app.example.com ("Cookie Tossing").
RFC 6265bis introduces special prefixes enforced by all modern browsers:
1. The __Host- Prefix (Maximum Security)
When a cookie name starts with __Host-, the browser rejects the Set-Cookie header unless:
- The connection is encrypted via HTTPS (
Secureflag present). - The
Pathattribute is explicitly set to/. - The
Domainattribute is omitted (preventing the cookie from being sent to subdomains).
// Correct __Host- cookie definition
res.setHeader('Set-Cookie', [
'__Host-auth_token=eyJhbGciOi...; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=86400'
]);
2. The __Secure- Prefix
When a cookie name starts with __Secure-, the browser only requires that the Secure flag is present and transmitted over HTTPS (it still allows custom Path and Domain values).
4. CSRF Defense Architecture: Double Submit & Custom Headers
While SameSite=Lax blocks most cross-site form POST attacks, defense-in-depth requires additional application-layer validation:
┌─────────────────────────────────────────────────────────────────────────────┐
│ CSRF Defense in Modern Web Architectures │
├──────────────────────────────────────┬──────────────────────────────────────┤
│ Single Page Apps (Next.js, React) │ Server-Rendered Forms (HTML / SSR) │
├──────────────────────────────────────┼──────────────────────────────────────┤
│ • Custom Request Header Verification │ • Cryptographic Anti-CSRF Tokens │
│ (e.g., `X-Requested-With` or │ • Encrypted Double Submit Cookie │
│ `X-CSRF-Token` headers) │ • Verifying `Origin` / `Referer` │
│ • Browsers enforce CORS preflight │ • Enforcing strict HTTP method checks│
│ for custom headers, preventing │ (`POST`, `PUT`, `DELETE` only) │
│ unauthorized cross-site forging. │ │
└──────────────────────────────────────┴──────────────────────────────────────┘
Origin Header Verification Middleware (Node.js / Express)
import { Request, Response, NextFunction } from 'express';
const ALLOWED_ORIGINS = new Set([
'https://app.wtool.dev',
'https://wtool.dev',
]);
export function verifyRequestOrigin(req: Request, res: Response, next: NextFunction) {
// Safe methods do not mutate server state
if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) {
return next();
}
const origin = req.headers['origin'] || req.headers['referer'];
if (!origin) {
return res.status(403).json({ error: 'Missing Origin or Referer header' });
}
try {
const originUrl = new URL(origin as string).origin;
if (!ALLOWED_ORIGINS.has(originUrl)) {
return res.status(403).json({ error: 'Cross-origin request blocked by CSRF policy' });
}
} catch {
return res.status(403).json({ error: 'Malformed Origin header' });
}
next();
}
5. Third-Party Embeds & CHIPS (Partitioned Cookies)
When embedding widgets, payment iframes, or devtools inside third-party domains, traditional third-party cookies are blocked by default.
CHIPS (Cookies Having Independent Partitioned State) allows servers to opt into cookie partitioning:
Set-Cookie: widget_theme=dark; Path=/; Secure; HttpOnly; SameSite=None; Partitioned
When Partitioned is present, the cookie is stored in a separate jar keyed by the top-level site (https://customer-portal.com), preventing tracking across unrelated third-party sites while maintaining session state within the embedded context.
6. Security Checklist & Diagnostic Tools
- Verify Header Directives: Inspect full cookie attributes and response security headers with the HTTP Headers Analyzer.
- Enforce CSP Policies: Combine secure cookie policies with restrictive script policies using the CSP Builder & Validator.
- Cryptographic Randomness: Generate cryptographically secure session IDs and CSRF secrets using the Password & Key Generator or verify token structures with the JWT Decoder.
- Subdomain Isolation: Always use the
__Host-prefix on primary authentication tokens to prevent subdomain hijacking.