There are many similar questions here on SO asking about this, but couldn't find one that would address my concerns about using HttpOnly cookies.
There are many answers that suggest using HttpOnly cookies reasoning that an XSS vulnerability on the site would allow an attacker to steal the JWT (or any auth) tokens from the LocalStorage, and this could be prevented by storing the token as a HttpOnly cookie.
I understand that, it kind of makes sense. An attacker with access to a user's auth token can impersonate that user on the targeted website.
However, if a website has an XSS vulnerability then the attacker can already do anything as the user without actually stealing the cookies. It can make requests to the site, steal user data, or perform restricted actions.
Is there anything an attacker can do with a stolen auth token that is not possible on the site as an XSS?
Given the above, is there a reason to prefer using cookies over the LocalStorage to store auth tokens?
(LocalStorage vs SessionStorage is a separate concern, I'm not necessarily interested in that comparison.)
Related
For a React project, I'm trying to implement some auth solution. The following are what I'm considering.
Session Cookies are random tokens saved in HTTP only cookies sent by
the server. They are vulnerable against CSRF attacks. To protect
against, CSRF attacks, send a unique identifier with every HTTP
request. XSS protection is still required.
Session JWT are random signed tokens sent by the server. They are
vulnerable against XSS attacks. No CSRF protection is necessary.
Based off that, I decided to implement a Session JWT due to it being a random token, only need to focus on XSS protection, and session management abilities. In fact, I don't see the point of http only session cookies since an XSS attack can act via the user's browser. Preventing cookie access is barely counted as protection since an XSS attack can fool the user into providing credentials.
However, the more I research into this topic, the more confused I become. I keep getting conflicted info like JWTs being compared to Sessions instead of Cookies, no Session JWT implementations, and comments that state Session Cookies protect against XSS attacks.
Am I misunderstanding Session, Cookies, and/or JWT based off my prior descriptions?
In a Authorization Code Grant flow, once a public client such as a Single Page Application (SPA) obtains a OAuth 2.0 access token, where should the SPA keep it?
Storing the access token in locale storage or session storage opens up to cross-site scripting (XSS) attacks, so that should be avoided.
Storing the access token in a non-httpOnly cookie also opens up to XSS attacks, so that should be avoided as well.
Storing the access token in a httpOnly cookie is not technically possible because that is the point of httpOnly.
So the only secure remaining option that I see is to keep it in memory. Is it actually secure? Is it the only secure way?
It's all about the risk you want to accept.
If you store it in a cookie, you potentially open up your application to CSRF. While it may make sense to exchange XSS for CSRF by storing the token in a httponly cookie, it doesn't make much sense to do so with a non-httponly cookie that besides CSRF is also vulnerable to XSS.
Storing it in localStorage or sessionStorage is ok in many cases. With choosing that, you accept the risk of XSS having access to tokens. To mitigate this risk, you might want to implement mitigations, like for example static security scanning with a suitable tool, regular pentesting and so on - security is not just code, it's also processes around how you create that code. With mitigations in place, you can decide to accept the residual risk.
You can also store tokens in memory, like for example in IIFEs I guess, from where it's somewhat harder to read in an XSS attack. Storing it in a plain variable doesn't help (javascript from XSS would still have access), and I'm not entirely sure about what the latest JS can do to securely make it inaccessible from outside a given object. It's probably not possible in a way that is actually secure.
Or you can go down a different route. You can store very short-lived access tokens in localStorage, accepting the risk of XSS having access. However, your IdP can issue refresh tokens in httponly cookies for the IdP domain. This way even if an access token is compromised, it is only valid for a limited amount of time, and then the attacker will not be able to renew it. This may make sense in some applications, and probably not in others.
In other words, is it dangerous to have a JWT, which doesn't contain any sensitive info, in a cookie without the httponly flag? I understand the main security concern is XSS attacks. So since Javascript can access the cookie, attackers can potentially access the token. But since there isn't any sensitive info in the JWT, is the only harm session hijacking?
The crux of the issue is that I would like the JWT to be in a Javascript-accessible cookie because I want to be able to access the token claims to limit the functionality of the user within the ui.
So is it a better idea to use a non-httponly cookie for the JWT, or make the JWT in a httponly cookie and just do a separate non-httponly cookie for storing the user's credentials?
Although the JWT does not contain any sensitive information, the token itself is sensitive. JWTs are usually used as bearer tokens, which means they are a (possibly time limited) credential that can be used by anyone in possession of the token to access whatever resources the token is issued for.
An attacker who was able to obtain the token via a successful XSS would therefore be able to impersonate the victim to make requests to your server. This is what you describe as "only" session hijacking. I'm not sure why you say "only". Usually session hijacking is pretty serious ;o)
A more secure approach in my opinion, as you say, to make the JWT cookie HTTP-Only and have a separate cookie containing the information used to limit the functionality in the UI.
As an aside, limiting the UI in this way should not be considered as an effective security measure on it's own, since it would almost certainly be possible to bypass the UI restrictions unless they were also enforced on the server side. It is fine to use it for personalisation of the UI though.
I am learning about JWT for the security of my project, but I have a question. If I recieve the token correctly after I did the login, but someone else (hacker) in other place steals this specific token, can he access to my session? The server that use JWT authentication is able to detect this and protect me? How?
Only the server should know the "secret" that is used to generate the JWT. If someone modifies the data contained in the JWT, the server will fail to decode it. So the server can trust any JWT that it can decode.
However, if a hacker got access to your computer, they could see the JWT that is stored in the browser and use it. This same threat exists w/cookies, so it's not really a flaw of the JWT.
One way to mitigate this threat is the expiration date of the JWT. For a banking app, your JWT might expire after a few minutes. For Facebook, it might expire after a few months. However, there's no bullet proof solution to this if someone gets access to your browser.
Another approach for hackers would be a "man in the middle" attack to intercept the network traffic between client and server and get at the cookie/JWT. The cookie/JWT should always be sent over HTTPS to prevent this.
IMPORTANT EDIT
Finally, to answer the question in your title, "How safe is JWT?": It depends on how you store the token. Local storage is not as secure as using cookies (reference) but cookies can be subject to CSRF or XSRF exploits.
This answer used to say JWT was safer than cookies, because cookies were subject to CSRF attacks. But storing JWT in local storage is not safe either. As a result, I'm no longer storing my JWT in local storage and using well known techniques to mitigate CSRF attacks.
This question already has answers here:
Where to store JWT in browser? How to protect against CSRF?
(7 answers)
Closed 2 years ago.
For the purpose of securing REST API using JWT, according to some materials (like this guide and this question), the JWT can be stored in either localStorage or Cookies. Based on my understanding:
localStorage is subjected to XSS and generally it's not recommended to store any sensitive information in it.
With Cookies we can apply the flag "httpOnly" which mitigates the risk of XSS. However if we are to read the JWT from Cookies on backend, we then are subjected to CSRF.
So based on the above premise - it will be best if we store JWT in Cookies. On every request to server, the JWT will be read from Cookies and added in the Authorization header using Bearer scheme. The server can then verify the JWT in the request header (as opposed to reading it from the cookies).
Is my understanding correct? If so, does the above approach have any security concern? Or actually we can just get away with using localStorage in the first place?
I like the XSRF Double Submit Cookies method which mentioned in the article that #pkid169 said, but there is one thing that article doesn't tell you. You are still not protected against XSS because what the attacker can do is inject script that reads your CSRF cookie (which is not HttpOnly) and then make a request to one of your API endpoints using this CSRF token with JWT cookie being sent automatically.
So in reality you are still susceptible to XSS, it's just that attacker can't steal you JWT token for later use, but he can still make requests on your users behalf using XSS.
Whether you store your JWT in a localStorage or you store your XSRF-token in not http-only cookie, both can be grabbed easily by XSS. Even your JWT in HttpOnly cookie can be grabbed by an advanced XSS attack.
So in addition of the Double Submit Cookies method, you must always follow best practices against XSS including escaping contents. This means removing any executable code that would cause the browser to do something you don’t want it to. Typically this means removing // <![CDATA[ tags and HTML attributes that cause JavaScript to be evaluated.
A timely post from Stormpath has pretty much elaborated my points and answered my question.
TL;DR
Store the JWT in cookies, then either pass the JWT in the Authorization header on every request like I've mentioned, or as the article suggests, rely on the backend to prevent CSRF (e.g. using xsrfToken in case of Angular).
Do not store your token in LocalStorage or SessionStorage, because such token can be read from javascript and therefore it is vulnarable to XSS attack.
Do not store your token in Cookie. Cookie (with HttpOnly flag) is a better option - it's XSS prone, but it's vulnarable to CSRF attack
Instead, on login, you can deliver two tokens: access token and refresh token. Access token should be stored in Javascript memory and Refresh token should be stored in HttpOnly Cookie. Refresh token is used only and only for creating new access tokens - nothing more.
When user opens new tab, or on site refresh, you need to perform request to create new access token, based on refresh token which is stored in Cookie.
I also strongly recommend to read this article: https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/
To help prevent CSRF attacks that take advantage of existing cookies, you can set your cookie with the SameSite directive. Set it to lax or strict.
This is still a draft and as of 2019 is not fully supported by all current browsers, but depending on the sensitivity of your data and/or your control over the browsers your users use, it may be a viable option. Setting the directive with SameSite=lax will allow "top-level navigations which use a 'safe'...HTTP method."