← Alle checks
BeveiligingTypische ernst: Hoog

Tokens in lokale opslag

Detecteert of je JavaScript tokens opslaat in localStorage/sessionStorage, kwetsbaar voor diefstal via XSS.

Wat het checkt

Deze scanner detecteert of je JavaScript sessie- of authenticatietokens opslaat in de localStorage of sessionStorage van de browser. Hij identificeert wanneer toegangsgegevens worden bewaard op een plek die vanuit de eigen code van de pagina bereikbaar is.

Waarom het belangrijk is

Data in localStorage is leesbaar door elke JavaScript die op de pagina draait, dus bij een XSS-lek kan een aanvaller het token stelen en zich voordoen als de gebruiker. Het is een veel minder veilige plek voor inloggegevens dan een goed beschermd cookie.

Hoe je het oplost

Sla sessietokens waar mogelijk op in cookies met de attributen HttpOnly en Secure, die JavaScript niet kan lezen. Moet je tokens op de client gebruiken, verkort dan hun levensduur en combineer dit met een stevige bescherming tegen XSS.

Hoe deze controle werkt

Scanaris leest het JavaScript van je homepage, inclusief de scripts die hij vanaf je domein laadt, en zoekt aanroepen van localStorage.setItem of sessionStorage.setItem waarvan de sleutel op inloggegevens wijst: token, jwt, auth, secret, password, api_key, access, refresh of bearer. Vindt het er een, dan opent het een bevinding met lage ernst. Laag, omdat een token in de browser bewaren op zichzelf geen inbraak is; het wordt er een in combinatie met welke scriptinjectie dan ook, en daarom is het de moeite waard om te weten.

Waarom het ertoe doet

Alles in localStorage kan gelezen worden door elke code die op je pagina draait: je eigen scripts, de scripts van derden die je laadt (analytics, chats, widgets) en elk script dat een aanvaller weet te injecteren. Eén XSS-kwetsbaarheid, in je code of in een afhankelijkheid, is genoeg om localStorage.getItem('token') naar een vreemde server te laten reizen en iemand de sessie van je gebruiker van elders te laten gebruiken, zonder wachtwoord en zonder dat hij het merkt. Een cookie met HttpOnly is onzichtbaar voor JavaScript, dus diezelfde XSS kan hem niet stelen; hij kan verzoeken doen zolang de pagina open is, wat erg genoeg is, maar hij kan de sessie niet meenemen.

Waar het meestal vandaan komt

Van de standaardconfiguratie van veel authenticatiebibliotheken in single-page-applicaties. De Supabase-client voor de browser bewaart de sessie in localStorage tenzij je anders zegt, net als veel "zelfgemaakte" JWT-voorbeelden met Express. Lovable, Bolt en v0 genereren dat patroon omdat het in de snelstartdocumentatie staat.

Hoe je het oplost

  • Sessie in HttpOnly-cookies. Heb je een server (Next.js, Nuxt, Express, een Edge Function), laat die dan het token van de provider ontvangen en in een cookie met HttpOnly, Secure en SameSite=Lax bewaren. De browser stuurt hem vanzelf mee en ziet hem nooit.
  • Met Supabase: gebruik in Next.js of Nuxt @supabase/ssr, dat de sessie in cookies bewaart; in een pure SPA zonder server accepteer je het risico en compenseer je het met een strikte CSP, want er is geen andere plek om hem te bewaren.
  • Korte tokens en veilige verversing. Moet het toegangstoken in het geheugen of in storage leven, laat het dan in minuten verlopen en zet het refresh-token in een HttpOnly-cookie.
  • Geheugen in plaats van storage. Het token in een JavaScript-variabele (niet in storage) bewaren beschermt het tegen scripts die storage lezen, maar niet tegen een actieve XSS op dezelfde pagina. Beter dan localStorage, slechter dan een cookie.
  • Verklein het XSS-oppervlak. Een CSP zonder 'unsafe-inline' in script-src, alle gebruikers-HTML opschonen en de scripts van derden die je laadt nakijken. Dat is de verdediging die het token in storage onbelangrijk maakt.

Controleer of het is opgelost

curl -s https://jouwdomein.com | grep -oE 'src="[^"]+\.js[^"]*"' | cut -d'"' -f2 | head -3
curl -s https://jouwdomein.com/assets/index-XXXX.js | grep -oE "(local|session)Storage\.setItem\([^)]{0,60}" | head

Geeft het tweede commando regels met token, auth of iets dergelijks in de sleutel, dan is het patroon er nog. In de browsertools, tabblad Toepassing, zie je wat je app na het inloggen in storage bewaart.

Veelgestelde vragen

Supabase bewaart de sessie standaard in localStorage. Is dat fout?

Het is het compromis dat een SPA zonder server sluit. Heb je een server, stap dan over op @supabase/ssr. Zo niet, let dan op de CSP en de scripts van derden: dat token is zo veilig als de pagina eromheen.

Is sessionStorage veiliger dan localStorage?

Alleen in duur: hij wordt gewist bij het sluiten van het tabblad. Elk script op de pagina leest hem net zo goed.

Waarom lage ernst als het de sessie kan stelen?

Omdat er een andere kwetsbaarheid (een XSS) nodig is om het te misbruiken. De controle waarschuwt je zodat je met kennis van zaken beslist; heeft je site al een strikte CSP en toont hij geen niet-opgeschoonde gebruikersinhoud, dan is het echte risico klein.

Check het op je site

Scan je site gratis en zie of deze en 44 andere checks slagen, met een fixprompt voor elk probleem.

Mijn site gratis scannen

Uit de blog

Gerelateerde checks