Session tokens in secure HTTP-only cookies, SameSite=Strict
Why it matters
Session tokens stored in localStorage or accessible JavaScript variables are exfiltrable by any XSS payload on any page of the application. A single reflected or stored XSS vulnerability anywhere on the domain becomes a full session hijack with no additional effort. CWE-614 (Sensitive Cookie Without 'Secure' Attribute) and CWE-1004 (Sensitive Cookie Without 'HttpOnly' Flag) define these as distinct failures. OWASP A07 and PCI-DSS Req 6.4.1 mandate that session tokens be inaccessible to client-side scripts. In a financial application, exfiltrating a session token from localStorage via document.cookie or XSS requires zero CSRF bypass — the token is already JavaScript-readable and portable.
Severity rationale
High because a session token in localStorage or a cookie missing HttpOnly is a single XSS vulnerability away from full account compromise — the attacker does not need to break HTTPS or defeat SameSite protections to extract and replay it.
Remediation
Configure session cookies with all three required attributes in src/lib/session.ts or the session middleware initialization:
session({
cookie: {
httpOnly: true, // inaccessible to document.cookie and XSS
secure: true, // HTTPS transport only
sameSite: 'Strict', // no cross-origin send (CSRF protection)
maxAge: 15 * 60 * 1000
}
})
Audit all calls to localStorage.setItem and sessionStorage.setItem in the codebase — no session token, auth token, or user identity value should appear as a storage key. Tokens are set exclusively by the server via Set-Cookie response headers; the client-side JavaScript never handles them.
Detection
- ID:
token-security-httponly - Severity:
high - What to look for: Count all cookie configuration settings and enumerate the security attributes: httpOnly, secure, sameSite. Quote the actual values found. Count all localStorage/sessionStorage token stores. Verify at least 3 cookie security attributes are set: httpOnly=true, secure=true, sameSite=Strict. A token stored in localStorage must not pass — do not pass if any session token is JavaScript-accessible.
- Pass criteria: Session tokens stored in cookies with at least 3 security attributes: httpOnly=true, secure=true, sameSite=Strict. Count all token storage locations — 0 must use localStorage/sessionStorage. Report the count even on pass (e.g., "Cookie config: httpOnly=true, secure=true, sameSite=Strict — 3 of 3 attributes set, 0 localStorage stores").
- Fail criteria: Session tokens in localStorage/sessionStorage (even 1 instance), or cookies lack any of: httpOnly, secure, or sameSite=Strict.
- Skip (N/A) when: The application uses stateless JWT with no cookies — cite the actual auth mechanism found.
- Detail on fail:
"Session token in localStorage — JavaScript-accessible. 0 of 3 required cookie attributes (httpOnly, secure, sameSite) applicable"or"Cookie has httpOnly=true but secure=false — 2 of 3 required attributes set" - Remediation: Use secure HTTP-only cookies (in
src/lib/session.tsor session middleware config):// session middleware configuration session({ cookie: { httpOnly: true, // Prevent JavaScript access secure: true, // HTTPS only sameSite: 'Strict', // Prevent CSRF maxAge: 15 * 60 * 1000, // 15 minutes domain: 'yourdomain.com' } }) // Never store tokens in localStorage // localStorage.setItem('token', sessionToken); // DON'T DO THIS // Cookies are set automatically by the server
External references
- cwe · CWE-614 — Sensitive Cookie in HTTPS Session Without 'Secure' Attribute
- cwe · CWE-1004 — Sensitive Cookie Without 'HttpOnly' Flag
- owasp:2021 · A07
- pci-dss:4.0 · Req 6.4.1 — Session management controls for public-facing web applications
Taxons
History
- 2026-04-18·v1.0.0·Initial import from finserv-session-security·automated