Session data not in localStorage; memory-only or secure cookie
Why it matters
localStorage persists across browser restarts, operating system sleeps, and shared-device sessions. Any XSS vulnerability on any page of the application — including third-party scripts loaded on marketing pages — can read localStorage across the entire origin and exfiltrate every stored token. CWE-922 (Insecure Storage of Sensitive Information) and CWE-312 (Cleartext Storage of Sensitive Information) both apply. OWASP A02 (Cryptographic Failures) addresses insecure data at rest. PCI-DSS Req 6.4.1 forbids storing sensitive authentication data where scripts can access it. In a financial application, a single-line localStorage.getItem('authToken') in an XSS payload yields a portable, replayable session credential.
Severity rationale
Low because the attacker requires a co-located XSS vulnerability to exploit localStorage exposure, but when that condition is met, every session token stored there is immediately and silently exfiltrable to an external server.
Remediation
Eliminate all localStorage.setItem calls that store authentication or session data. Use React Context for in-memory state and rely on server-set HTTP-only cookies for the session token. In src/context/AuthContext.tsx:
const AuthContext = createContext<{ user: User | null }>({
user: null
});
export function AuthProvider({ children }: { children: React.ReactNode }) {
// User state lives only in memory — cleared on page refresh
const [user, setUser] = useState<User | null>(null);
return (
<AuthContext.Provider value={{ user }}>
{children}
</AuthContext.Provider>
);
}
// Never: localStorage.setItem('authToken', token);
// Never: sessionStorage.setItem('authData', JSON.stringify(user));
Grep the codebase for localStorage.setItem and sessionStorage.setItem — any hit containing token, auth, session, user, or jwt in the key name is a finding that must be removed.
Detection
- ID:
session-data-not-local-storage - Severity:
low - What to look for: Count all localStorage.setItem and sessionStorage.setItem calls that store auth/session data. Quote the actual storage key names found (e.g., 'token', 'sessionToken', 'authData'). Enumerate the secure alternatives used (HTTP-only cookies, React state, Context). Any session token in localStorage must not pass — do not pass if any sensitive token is stored in localStorage.
- Pass criteria: Session tokens stored only in memory (React state, Context) or secure HTTP-only cookies. Count all localStorage/sessionStorage calls — 0 must store sensitive session tokens. Report the count even on pass (e.g., "0 localStorage session stores, auth data in React Context + HTTP-only cookie").
- Fail criteria: Any session token or sensitive data in localStorage (even 1 instance), or sessionStorage used for persistent auth tokens.
- Skip (N/A) when: The application is a static site with no session management — cite the actual architecture found.
- Detail on fail:
"1 localStorage.setItem('authToken', ...) found in src/lib/auth.ts:23 — session token exposed to XSS" - Remediation: Store session data securely (in
src/lib/auth.tsorsrc/context/AuthContext.tsx):// DON'T DO THIS: // localStorage.setItem('sessionToken', token); // sessionStorage.setItem('authData', JSON.stringify(user)); // DO THIS instead: // Use HTTP-only cookies (set by server) // Or use memory with Context API // lib/auth.ts (React Context example) const AuthContext = createContext<{ user: User | null }>({ user: null }); export function AuthProvider({ children }) { const [user, setUser] = useState<User | null>(null); // User data in memory, never persisted to localStorage return <AuthContext.Provider value={{ user }}>{children}</AuthContext.Provider>; }
External references
- cwe · CWE-922 — Insecure Storage of Sensitive Information
- cwe · CWE-312 — Cleartext Storage of Sensitive Information
- owasp:2021 · A02
- 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