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
- we need to mitigate the risks of XSS by following OWASP XSS prevention guidelines in the first place.
- Also, the
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:
- Use a
SameSite
cookie. Refer the doc here. If the browser doesn't support SameSite
cookie, follow another approach from below
- State Variable (Auth0 uses it) - The client will generate and pass with every request a cryptographically strong random nonce which the server will echo back along with its response allowing the client to validate the nonce. It's explained in Auth0 doc.
- Use a CSRF cookie with a cryptographically strong random value such that every AJAX request reads the cookie value and add the cookie value in a custom HTTP header (except GET and HEAD requests which are not supposed to do any state modifications). Since CSRF cannot read anything due to same origin policy and it is based on exploiting the unsafe HTTP methods like POST, PUT and DELETE, this CSRF cookie will mitigate the CSRF risk. This approach of using CSRF cookie is used by all modern SPA frameworks. The Angular approach is mentioned here
- Always check the referer header and accept requests only when referer is a trusted domain. If referer header is absent or a non-whitelisted domain, simply reject the request. When using SSL/TLS referrer is usually present. Landing pages (that is mostly informational and not containing login form or any secured content may be little relaxed and allow requests with missing referer header
- TRACE HTTP method should be blocked in the server as this can be used to read the httpOnly cookie
- Also, set the header
Strict-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-domain