We need to save the JWT on the client computer. If we store it in LocalStorage / SessionStorage, it can be easily captured using an XSS attack. If we store it in cookies, then the hacker can use it (without reading it) in a CSRF attack and impersonate a user and turn to our API and send requests for actions or receive information on behalf of the user.
But there are several ways to protect JWTs in cookies so that they are not easily stolen (but there are still some advanced methods to steal them). But if you want to rely on LocalStorage / SessionStorage, then it can be accessed with a simple XSS attack.
So, to solve the CSRF problem, I use Double Submit Cookies in my application.
Double cookie method
Store the JWT in the HttpOnly cookie and use it in secure mode to transmit over HTTPS.
Most CSRF attacks have different origin or referrer headers with the source host in their requests. So check if you have any of them in the header, they come from your domain or not! If you do not reject them. If the request does not have a source and a source of links, then do not worry. You can rely on the result of checking the X-XSRF-TOKEN header, which I will explain in the next step.
While the browser will automatically provide your cookies for the request domain, there is one useful limitation: the JavaScript code that runs on the website cannot read the cookies of other websites. We can use this to create our CSRF solution. To prevent CSRF attacks, we need to create an additional Javascript readable cookie called: XSRF-TOKEN. This cookie must be created when the user logs in and must contain an arbitrary string with invalid values. We also store this number in the JWT itself as a special requirement. Every time a JavaScript application wants to make a request, it will need to read this token and send it to the custom HTTP header. Since these operations (reading the cookie, setting the header) can only be performed in the same domain of the JavaScript application, we can know that this is performed by the real user who uses our JavaScript application.
Angular JS makes your life easier
Fortunately, I use Angular JS in our platform, and Angular is an approach to CSRF tokens, which simplifies the implementation. For every request that our Angular application makes on the server, the Angular $http will do this automatically:
- Locate the cookie named XSRF-TOKEN in the current domain.
- If this cookie is found, it reads the value and adds it to the request as the X-XSRF-TOKEN header.
Thus, client-side implementation is performed automatically, automatically! We just need to set a cookie called XSRF-TOKEN in the current domain on the server side, and when our API receives any call from the client, it should check the X-XSRF-TOKEN and compare it with XSRF-TOKEN in JWT. If they match, then the user is real. Otherwise, this is a forged request, and you can ignore it. This method is inspired by the Double Submit Cookie method.
Attention!
In reality, you are still XSS prone, it is just that an attacker cannot steal your JWT token for later use, but it can still make requests on behalf of your users using XSS.
Whether you store the JWT in localStorage or you store your XSRF token in a non-HttpOnly cookie, both can be easily captured by XSS. Even your JWT in an HttpOnly cookie can be captured by an extended XSS attack, such as the XST method .
Therefore, in addition to the Double Submit Cookies method, you should always follow the XSS guidelines, including escaping content. This means removing any executable code that causes the browser to do what you do not want. This usually means removing the // <![CDATA[ tags and HTML attributes that cause JavaScript to be evaluated.
More details here: