However, these locations are vulnerable to XSS (Cross-Site Scripting) attacks.
However, wrapping tokens/credentials in cookies open up a vulnerability to CSRF (Cross-Site Request Forgery) attacks. What that new beast is capable of is best explained with the following very popular scenario:
“On one tab of his browser, a user is logged in on his bank account SPA which returned an HttpOnly Secure Cookie to the browser. On every request the user makes to his bank, the cookie will be passed by the browser on to the API backing the bank SPA to prove he is authorized to make those calls.
In the meantime, the user opens up another tab into a malicious website inviting him to click on a video of a kitten stuck in a fridge. By clicking on the video, the malevolent website will send a request to the user’s bank, asking to transfer all his savings to the hacker’s account. The browser, seeing that the destination of the request corresponds to the HttpOnly Secure cookie issued to the user when he logged in to the bank, will happily pass the cookie. The bank API will unwrap the cookie, notice the token/credentials are legit and execute the money transfer.
The hacker successfully committed her mischief and can now enjoy an early retirement plan.”
So, in a nutshell, we face the following dilemma:
- Tokens/credentials wrapped in HttpOnly Secure Cookies are immune to XSS but vulnerable to CSRF
The Best of Both Worlds
To be resistant to both XSS and CSRF attacks, the most common mitigation strategy roughly follows those lines:
- Add a header with a randomly generated string (often called CSRF token, or state in OAuth2 terms)
- Add that same randomly generated string to the token/credentials (most easily achieved as an additional claim when using JWT, i.e. Json Web Tokens)
- Wrap the tokens/credentials in a HttpOnly Secure Cookie
- Ensure the API verifies the randomly generated string contained in both the header and the cookie do match
→ XSS will not work because of the HttpOnly Cookie
→ CSRF will not work because the attacker does not know which random string to send in the header together with the HttpOnly Secure Cookie
One can still face the scenario of an attacker getting the CSRF token through an XSS attack, and passing it to a hidden request on the attacker’s malicious website, forcing the browser to pass the HttpOnly Secure Cookie to the request.
This scenario, albeit possible, is much less likely to arise in practice, as this attack is a much more specifically targeted and thoroughly engineered one.
OAuth2 Implicit flow
Perhaps of bigger concern is the implicit flow defined by the OAuth2 standard.
This flow specifically targets SPAs, and mandates the authorization servers implementers to return tokens directly to the SPAs, hence making them vulnerable to XSS attacks.
This pattern has been praised for several years now, and only in 2018 has this flow started to be revised to tackle that vulnerability.
The problem is that most businesses do not have the time nor the resources to build and maintain authentication and authorization servers. Luckily, to the rescue come the AaaS (Authentication as a service) providers such as Auth0, AWS, Google and so forth, which businesses have heavily relied on these last years.
Unfortunately, these providers are not up to date with the latest reforms of the OAuth standard and they do not necessarily strictly follow them. And, AFAIK, this flow has not been revised by the main AaaS implementers in mid 2019. Moreover, AaaS implementers control how tokens are being sent to the applications. Therefore, businesses relying on AaaS cannot change the flow to return both CSRF tokens and token wrapped HttpOnly Secure Cookies.
For a more involved dig in the OAuth2 implicit flow plague, I warmly encourage keen readers to go through the following post: https://auth0.com/blog/oauth2-implicit-grant-and-spa/ of Vittorio Bertocci, principal architect of one of the main AaaS providers. The post extensively goes through the OAuth2 journey and demonstrates the current lack of effective solution to securely use the implicit flow today.
By Tanguy Naets
Machine Learning Engineer at Radix.ai