I am trying to implement a user friendly anti CSRF mechanism.
Currently my application program sets a cookie and session variable with the anti-csrf token and sends it to user.
Whenever the user makes an unsafe request(POST,DELETE,PUT) javascript reads the cookie and adds the token to the form which is sent via an ajax request
On server the form value is compared with session contained value.
Problem is my application will be open in multiple tabs and it it highly probable the the token will expire on server.
Is it a good practice to get new csrf tokens from a server file like
get-csrf-token.php
Because anyways the attacker cannot read the response from cross site requests(considering jsonp and cors is disabled)
EDIT:
I plan to keep single CSRF token valid per hour per session and the web applications will re-request new tokens after an hour
Is there anything wrong with this approach?
You only need one CSRF token per user session. As any attacker cannot read the token due to the Same Origin Policy, you do not need to refresh the token until the next session is active. This will mean there will be no problems with your application being open in multiple tabs.
Your approach is an implementation of the Synchronizer Token Pattern CSRF protection mechanism, which is the OWASP recommended approach. As JavaScript is used to add the value to the request, you can't mark your cookie as httpOnly. This would have prevented any XSS vulnerabilities from allowing an attacker to grab your cookie value. However, if you do have any XSS vulnerabilities, these are slightly more serious than CSRF ones and should be addressed immediately anyway as there are other attack vectors once an XSS flaw is found.
See this post for some pros and cons of some CSRF mechanisms: Why is it common to put CSRF prevention tokens in cookies?
In my project, I use cookies for managing authentication of users, and use session for generating the CSRF token.
When generating the form, it should be included in hidden field. For ex:
<form method="post" action="/paymoney">
<input type="hidden" name="csrf" value="csrf value" />
...
</form>
When user makes an request, the server should authenticate the request first (via cookie). After that, the server get the correct user session and verify the CSRF token.
Note that, you should care about the time out of CSRF token. The more expired time of this token, the less efficiency you can get. But if the expired time is too short, it cause a trouble that some ajax call can not work although the authentication of user is still valid.
Related
This article suggests that we should be changing our CSRF tokens on every request to prevent a BREACH attack. i.e., if we use gzip/brotli and per-session CSRF tokens, and SSL, our tokens are vulnerable with only 1000 requests.
Supposing that's true, how would one go about regenerating a CSRF token on every request without breaking back/forward and multiple tabs?
The obvious solution is to store an array of valid CSRF tokens in our session instead of just the most recent one, perhaps limiting it to 100 or so.
But what if we used a JWT or something instead? We could store just the user ID in there, then validate that the token isn't expired and it belongs to the current user, and we wouldn't need to store it on the server at all. The only problem is that we couldn't revoke the CSRF-JWT when the user logs out, which would necessitate a short expiry, but we wouldn't want it too short or it'd expire before the user has a chance to submit the form.
What's the best way to approach this problem?
You can use a JWT and send it as a Bearer token in the header and store it in local storage. Do NOT send it in a cookie. Send it on every request and check the validity of it on every request. You can give the user a refresh token that you can revoke when they log out. The refresh token will generate access JWTs that expire after a very short period for each call.
You can use the JWT for access control and all your authorization needs.
I've been reading that "remember me" cookies are stored in "httpOnly" cookies, so they are not accessible by JavaScript/XSS. However, "httpOnly" cookies are vulnerable to CSRF attacks because they are sent with the request automatically.
To mitigate the CSRF attack, it is recommended to use the synchronized tokens pattern (have the server generate csrf tokens and crosscheck with the client) .
My question is, if a "remember me" cookie is available, is it possible for a CSRF attack (malicious JavaScript) to make a request and subsequently obtain the csrf token generated from the server? The concern is, if an attack has the cookie as well the token to send with requests, then the security of the app has been compromised.
If this is indeed possible, how could we prevent this?
No, the token cannot be read by another domain due to the Same Origin Policy.
If the request is made server-side to bypass the SOP, then the server isn't getting the token from the victim's browsing context therefore this cannot attack the logged in user (the server could only attack their own user that they used to get the token with).
Therefore, nothing to worry about (as long as you haven't enabled CORS of course).
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."
I have an application in which I would like to implement protection against CSRF using a security token, but also to make my application available for that same user if he opens a new tab.
When the user authenticates himself with his correct username/password combination, I add him to the session and return a cookie that contains the token. When the cookie arrives, I remove the token from the cookie and store it in a global variable. With each request I make I append the token and compare it with the one on the server.
The problem is when I open a new tab, user gets automatically removed from the session because a request that doesn't contain a correct token is received.
I understand that if I store that token in the cookie or in the localStorage I would be able to read it from another tab and the request will be valid, but I'm not sure how safe is this implementation or even which one is better? With a simple XSS you could read the token from the cookie/localStorage/global variable...
Are there any other ways I can implement a CSRF token protection and still be able to use my application from another browser tab?
With a simple XSS you could read the token from the cookie/localStorage/global variable...
If your site is vulnerable to XSS then this always supersedes any CSRF vulnerability.
As long as CSRF tokens are refreshed for every new session, there is no need to change the CSRF token once it has been used. An attacker cannot read the token so there is no extra risk.
This will enable tokens to work across tabs with no loss in security.
I've recently run into an interesting problem with login pages and CSRF tokens. I want to ensure the login form POST is secured with a CSRF token, however, when/if a user remains on the login page for an extended period of time his/her session will expire and the CRSF token will become invalid. Any advice for how to avoid this issue? I am considering not using a CRSF token for login pages, but this seems to be a bad practice.
Technically speaking, the login page is an out-of-session page (the user hasn't logged in yet) and therefore a CSRF mitigation isn't really needed. There's not a whole lot a hacker can do if the user hasn't established a session. I guess he could trick a user into logging on-- if he knows the user name and password-- but if could do that he could log in from his own browser instead.
If you insist on the CSRF token on the login page, I suggest you render the token as per usual and refresh the page with a Javascript timer (setTimeout) a few seconds before the token is due to expire.
Wikipedia page on CSRF here mentions that a special category of CSRF known as login CSRF is possible. I quote from the page itself
An attacker may forge a request to log the victim into a target
website using the attacker's credentials; this is known as login CSRF.
Login CSRF makes various novel attacks possible; for instance, an
attacker can later log into the site with his legitimate credentials
and view private information like activity history that has been saved
in the account. The attack has been demonstrated against YouTube.
Also a very popular Java MVC framework (Spring MVC) in its recent releases added inbuilt CSRF protection using CSRF tokens, and they too recommend using the login form to be CSRF protected. Again I quote from here
In order to protect against forging log in requests the log in form
should be protected against CSRF attacks too. Since the CsrfToken is
stored in HttpSession, this means an HttpSession will be created
immediately. While this sounds bad in a RESTful / stateless
architecture the reality is that state is necessary to implement
practical security. Without state, we have nothing we can do if a
token is compromised. Practically speaking, the CSRF token is quite
small in size and should have a negligible impact on our architecture.
You should check out the Encrypted Token Pattern when considering CSRF protection methods. It's stateless by design and requires a single Token only, as opposed to the Synchronizer Token or Double Submit Cookie Patterns, which compare two tokens.
In terms of your problem with Tokens expiring on the Login page, you can leverage a framework called ARMOR to protect against CSRF. I wouldn't worry about the login page in terms of CSRF, as you generally don't provide the option for a user to change state at this stage, which is what CSRF is all about. It might be worth considering injecting the Token after the user has logged in, in your case.
Also, the official OWASP Cheat Sheet is a good point of reference.