Security finding
A JSON Web Token in the browser is unsigned, never expires, lives long, or sits in web storage
Security Warning Severity: Medium
Part of the audit Session tokens readable in the browser. Finding id: security.jwtInBrowser.
What it costs you
Whoever holds a session token is logged in as that visitor.
What to do
Sign tokens, give them a short lifetime, and keep them where scripts cannot read them.
What it checks
- Login token has no signature
- Login token never expires
- Login token lasts too long
- Login token kept in browser storage
How to fix it
Sign the token and check the algorithm
- Find the code or service that issues the token held in [container] and have it sign tokens with a key, for example with HS256 or RS256.
- In the code that checks tokens, accept only the algorithm you sign with and reject alg none. Most JWT libraries take the allowed algorithms as a setting.
How to check: Decode the header of a new token in your browser's developer tools: alg is not none.
Give the token a short lifetime
- Set an exp claim on every token the browser holds, a short time after it is issued.
- If visitors must stay logged in longer, issue a new short token from a refresh token kept in an HttpOnly cookie.
- Have the server reject tokens without exp or past it.
How to check: Decode a new token: exp is present and close to iat.
Move the token out of web storage
- Have the server set the token, or the session it stands for, as a cookie marked HttpOnly, Secure and SameSite=Lax, instead of handing it to a script that writes [where found].
- If a script must hold the token, keep it in a variable in memory only and get a new one after a reload.
- Remove the code that writes [container] to [where found], and delete the key on the next visit.
How to check: Open the developer tools on [example url], Application tab: [where found] has no key [container].
Thresholds
| Name | Default | Meaning |
|---|---|---|
max_lifetime_hours | 24 | Longest acceptable lifetime of a token the browser holds, in hours. Starting value, not taken from a standard. |