Auth0 provide extensive list of resources describing best practices for the authentication. Among them there's a constant stream of advice not to use localStorage
as a mean to store (JWT) tokens.
I've found several issues with those points:
auth0.getTokenSilently()
from their SDK to obtain the token, but as far as I see, there shouldn't be any reason why attacker couldn't call this method themselves (i.e. injecting another sdk script, using the existing client keys, and just calling the getToken from there), thus obtaining the token in pretty much the same manner as if it was stored in localStorage
So I fully agree with OWASP recommendation, not to store sensitive data in localStorage
, I would never think of storing credit card numbers or passwords or even personal data there. But that's only because those things would allow attacker to expand the scope of their attack (access the bank account, try to reuse user's passwords in other apps, etc.). But I struggle to see how are accessTokens affected by this.
If you store it inside localStorage, it's accessible by any script inside your page. This is as bad as it sounds; an XSS attack could give an external attacker access to the token. To reiterate, whatever you do, don't store a JWT in local storage (or session storage).
Auth0 recommends storing tokens in browser memory as the most secure option. Using Web Workers to handle the transmission and storage of tokens is the best way to protect the tokens, as Web Workers run in a separate global scope than the rest of the application.
Tokens received from OAuth providers are stored in a Client Access Token Store. You can configure client access token stores under the Libraries > OAuth2 Stores node in the Policy Studio tree view.
A JWT needs to be stored in a safe place inside the user's browser. Any way,you shouldn't store a JWT in local storage (or session storage). If you store it in a LocalStorage/SessionStorage then it can be easily grabbed by an XSS attack.
Although I don't know much about Auth0 implementations, features and design decisions, from my general understanding of OAuth2 and security, I can try connecting the dots.
A single recommendation by itself doesn't provide enough security or desired functionality, but when used with a combination of other related recommendations, we can achieve the desired level of security and behavior.
Let's go through the points you raised.
From my perspective accessing the tokens itself doesn't extend the scope of attack. If the attacker has control over the victim's browser they can execute the calls, using the token, from the affected browser itself
The problem with localstorage
is:
localStorage
and sessionStorage
are not shared across sub-domains. This is show stopper for SSO functionality (There are some solutions using iframe
, but those look more like workarounds rather than a good solution. And when the response header X-Frame-Options is used to avoid clickjacking attacks with iframe
, any solution with iframe
is out of question)
XSS can send the access and/or refresh tokens to remote servers controlled by the attacker and thus allowing the attacker to impersonate the user
Note: The vulnerability mentioned in point 2 can be mitigated by using a Sender Constrained Access Tokens approach where the client has to prove that they indeed own the token. Another alternative is the fingerprint approach mentioned in OWASP which needs a cookie. However, it seems Auth0 doesn't implement any of these. Therefore, the recommendation of avoiding localstorage
makes sense.
Auth0 recommends using auth0.getTokenSilently() from their SDK to obtain the token, but as far as I see, there shouldn't be any reason why attacker couldn't call this method themselves
Correct. That's why
getTokenSilently()
method requires you to have Allow Skipping User Consent
enabled in your API Settings in the Dashboard. Although I don't see a specific guideline on this, but I think if you store the token in cookies you don't need this option to be enabled and thereby eliminating any possibility of misuse of the method.The only way that I know where XSS wouldn't be able to access the tokens is basically using httpOnly cookies, but that creates new vectors by itself (CSRF) and still wouldn't prevent attackers from calling the api from within the affected browser
True. But you can mitigate this with one or a combination of the following approaches:
SameSite
cookie. Refer the doc here. If the browser doesn't support SameSite
cookie, follow another approach from belowStrict-Transport-Security: max-age=<expire-time>; includeSubDomains
to allow only secured connections to prevent any man-in-the-middle overwrite the CSRF cookies from a sub-domainIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With