← All checks
SecurityTypical severity: High

Token Storage Scanner

Detects whether your JavaScript stores tokens in localStorage/sessionStorage, vulnerable to XSS theft.

What it checks

This scanner detects whether your JavaScript stores session or authentication tokens in the browser's localStorage or sessionStorage. It identifies when access credentials are kept in a place reachable from the page's own code.

Why it matters

Data stored in localStorage is readable by any JavaScript running on the page, so if there is an XSS flaw an attacker can steal the token and impersonate the user. It is a far less safe place for credentials than a properly protected cookie.

How to fix it

Whenever possible, store session tokens in cookies with the HttpOnly and Secure attributes, which JavaScript cannot read. If you must use tokens on the client, shorten their lifetime and pair this with solid protection against XSS.

How this check works

Scanaris reads the JavaScript of your home page, including the scripts it loads from your domain, and looks for calls to localStorage.setItem or sessionStorage.setItem whose key suggests a credential: token, jwt, auth, secret, password, api_key, access, refresh or bearer. If it finds one, it opens a low-severity finding. It is low because storing a token in the browser is not a breach by itself; it becomes one when combined with any script injection, and that is why it is worth knowing.

Why it matters

Everything in localStorage can be read by any code running on your page: your scripts, the third-party ones you load (analytics, chats, widgets) and any script an attacker manages to inject. A single XSS vulnerability, in your code or in a dependency, is enough for localStorage.getItem('token') to travel to a foreign server and for someone to use your user's session from elsewhere, without their password and without them noticing. A cookie with HttpOnly is invisible to JavaScript, so that same XSS cannot steal it; it can make requests while the page is open, which is bad, but it cannot take the session away.

Where it usually comes from

From the default configuration of many authentication libraries in single-page applications. The Supabase browser client stores the session in localStorage unless told otherwise, as do many "hand-rolled" JWT examples with Express. Lovable, Bolt and v0 generate that pattern because it is the one in the quick-start documentation.

How to fix it

  • Session in HttpOnly cookies. If you have a server (Next.js, Nuxt, Express, an Edge Function), let it receive the token from the provider and store it in a cookie with HttpOnly, Secure and SameSite=Lax. The browser sends it by itself and never sees it.
  • With Supabase: in Next.js or Nuxt use @supabase/ssr, which stores the session in cookies; in a pure SPA without a server, accept the risk and compensate with a strict CSP, because there is nowhere else to keep it.
  • Short tokens and safe refresh. If the access token has to live in memory or in storage, make it expire in minutes and keep the refresh token in an HttpOnly cookie.
  • Memory instead of storage. Keeping the token in a JavaScript variable (not in storage) protects it from scripts that read storage, though not from an active XSS on the same page. Better than localStorage, worse than a cookie.
  • Reduce the XSS surface. A CSP without 'unsafe-inline' in script-src, sanitising any user HTML and reviewing the third-party scripts you load. That is the defence that makes the token in storage not matter.

Check it is fixed

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

If the second command returns lines with token, auth or similar in the key, the pattern is still there. In the browser tools, Application tab, you can see what your app stores after logging in.

Frequently asked questions

Supabase stores the session in localStorage by default. Is that wrong?

It is the trade-off a serverless SPA makes. If you have a server, switch to @supabase/ssr. If not, take care of the CSP and the third-party scripts: that token is as safe as the page around it.

Is sessionStorage safer than localStorage?

Only in duration: it is cleared when the tab closes. Any script on the page reads it just the same.

Why is it low severity if it can steal the session?

Because it needs another vulnerability (an XSS) to be exploited. The check warns you so you decide with information; if your site already has a strict CSP and shows no unsanitised user content, the real risk is small.

Check it on your site

Scan your site free and see if this and 44 other checks pass, with a fix prompt for every issue.

Scan my site free

From the blog

Related checks