How worried should I be about opening up a JWT to an XSS vulnerability?

0

Issue

I am building a node.js web application with react for the the GUI and graphQL served with Apollo for the back-end connecting to a RDS (MySQL) instance on AWS.

I am authenticating users and then returning JWTs. I have it figured out on how to renew/expire tokens, but now I am being faced with the question where to save it on the client side when a user visits the site…

There are two main concepts with a third being a hybrid model. 1) Store it as localStorage with JavaScript as described on HowToGraphQL 2) Store it in a Cookie with http-only set to true as described in the afore mentioned article as a cationary reference to Randall Degges

There is another alternative to store it in memory only on the client side but then a user would have to login every time the page is refreshed as it would not be persistent anywhere.

Concept 1 is vulnerable to XSS only if there is another XSS vulnerability already exploited. But it is secure to the site only so only scripts running on the site can access it and not scripts on any site. There it a lot of security talk that it should not be stored this way even though it is the common way because a developer cannot trust EVERY JavaScript script they are running on their site and there may be one that reads the localStorage and then sends it offsite.

Concept 2 removes the XSS vulnerable by declaring the http-only to only make it accessible to the server at your site. The problem here lies in that then a separate method has to be created to use the same backend authentication for other uses such as a standard API (for native apps or other sites) where the JWT is sent in the header over https where it is stored securely on another server.

So I researched and found this hybrid method described by Ben Awad 3) use a request token and a refresh token. The request token can then act normally for the standard API but then also on our react app site we can store it only in memory and store a refresh token in a cookie to send back a request token when users refresh or close and reopen browsers.

So theoretically, the best solution is Concept 3 which solves all of the concerns, but it is of course more complicated to setup.

My question: How worried should I be about opening up a JWT to an XSS vulnerability? It is something that down the road I would do the long way when I have more time, but I am pushing for a deadline. My site will be lesser known and not something like Facebook or Sales-Force that hackers would necessarily target. My site is not storing Credit Card data or other highly sensitive data other than a basic CRM and task list. If my site was open to XSS through other code, wouldn’t the entire authentication process be vulnerable through keylogging scripts or the likes without even knowing the JWT. I feel like I would be doing a lot of extra work to secure against a possible threat that if occurred, the entire system would be compromised already.

Solution

This is the issue I spent a lot of time on. How to store the authorization token securely. People have different strategies in dealing with this so I will share what works for me. Users of my apps were targeted by different attacks, all of them where unsuccessful in stealing anything so far. None used XSS.

Here is what I do

In the end I opted for storing authorization token in local storage. Applications that I work usually have WebSocket connections on top of HTTP routes and I want the token to be saved in one place and act as a single source of truth. They are all web applications running in the browser. Most of the applications I build use JWT.

Why I do it like that

First why I don’t use refresh tokens. If they are saved the same way as the actual authorization token would be saved negates the reason for refresh token to exist since the attacker can use the refresh token to get the authorization one.

Storing the token in cookies gives no benefits over local storage assuming that the app is secured against attackers being able to inject JavaScript into your app, mostly through forms and api on your app. Make sure all user inputs are JS injection safe. On top of that with cookies there are issues when using WebSockets that you must go around.

There is also the point of one of the accounts being hacked and you want to invalidate that token as soon as possible. JWT by default has no mechanism of being revoked. Implementing this feature negates the scalability of JWT because checking the JWT would require a call to the database to know if that user can do the specific action. There are 2 ways you can go about this. One is just check the user data if the user is frozen from the database, it is less scalable because of the call but if you already pull the user data in a middleware it is good enough TM. Other is to pull the the "is the user frozen" data from the database just when making changes to the database or when the call from the client is important.

In summary

I would store the token in local storage. Secure the app from code injections. And make a kill switch for the accounts if they get compromised in any way.

EDIT THANKS TO THE COMMENTS BY @JerryCauser

It is more secure to keep your token in a secure http only cookie. Don’t expect a storage mechanism choice to automatically save your users from being hacked. There are ways to hijack sessions and other exploits including users using web extensions and approving their request to read protected data.

For the example of the betting website below, you wouldn’t require user to write their password (or approve the request via automated email) every time they place a bet, but you would every time they want to take a withdrawal for example.

I use local storage because even if it happens for the token to be stolen, or another person got to your user’s laptop (like a kid for example) you should never let the account do critical tasks without approval.

There is no magic bullet of anti hack protection. Try your best to keep your users safe with common sense.

EDIT AS ANSWER TO THE COMMENT FROM THE ASKER @amaster

If you are making a trip to the database on every call, maybe JWT is not the best solution. Point of JWT is to have signed claims and the id of the user without calling the database. In this case, maybe opt in for sessions instead of JWT.

Answered By – Cedomir Rackov

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave A Reply

Your email address will not be published.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More