DON'T STORE PASSWORDS IN LOCALSTORAGE

also not in sessionStorage, IndexedDB, cookies (non-HttpOnly), your JS bundle, HTML comments, README.md, console.log, Slack, or that post-it on your monitor 📝

Okay, so… how do I do it right?

Why this is cursed client-side secrets

  • Any JS (yours or a compromised dependency) can read client storage → XSS = instant exfiltration.
  • Tokens/passwords persist across tabs and time; CSP won’t save you from already-exposed secrets.
  • DevTools, extensions, and third-party scripts can access it. 🔓
  • Users share devices, sync browsers, and export profiles. Congrats, your secret is now on tour.

Do this instead server-side sessions

Set-Cookie: session=opaque-session-id; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=1800
  • Use server-managed sessions with HttpOnly + Secure + SameSite cookies.
  • Rotate session IDs on login; expire & revoke on logout. Consider short TTLs.
  • Protect against CSRF (SameSite, double-submit, or synchronizer tokens).
  • Gate sensitive data server-side; treat the client as compromised by default.

Crimes (do not commit)

// ❌ localStorage localStorage.setItem("password", "hunter2"); // ❌ sessionStorage sessionStorage.token = jwt; // ❌ Cookies without HttpOnly // document.cookie = "token=" + jwt; // ❌ In source control // const API_KEY = "ABCD-1234-LEAKED";

Bonus offense: committing secrets to a public repo and adding a sticker that says “No peeking”.

Minimal “do it right” login flow

Client → POST /login (over HTTPS) Server → Verify creds → Create session in DB/Redis → Set HttpOnly cookie Client → Subsequent requests send cookie automatically Server → Authorize per-request based on session → Return data Logout → Server deletes session + sets expired cookie

Need APIs across subdomains? Use SameSite=None; Secure with strict CORS, and still keep tokens out of JS.

Dev Meme Wall