I was trying to implement CSRF Protection in my new Project. I did the same using creating a session token for every form and the token is stored in a hidden field in the form. Every time the form gets submitted, i check whether the token in the POST and token in the session are the same. If they are the same the required action is done and the session token is updated.
It works well but the actual problem arises when we refresh the submitted page. Token mismatch then occurs.
My question is, is it secure using a single token for each form, and without updating the token for every form submit? Will this be able to prevent CSRF?
Yes, there is no need to update the CSRF token. It just needs to be unique per user session.
As there is no way for an attacker to read the value of the hidden form field, the same value can be reused during the session. No extra security is accomplished by renewing this value.
However, it should definitely be a unique value per user session. Different users should have different tokens, and if the same user logs in again it would be a good idea to expire the previous token.
Related
After doing some research in using JWT with Access Token and Refresh Token for authentication. I understand this in this way.
After login, return to user Access Token and Refresh Token (using same technique JWT for both).
Saving Refresh Token in Database (one User can have multiple Refresh Tokens for multiple devices).
Whenever user sends a request with invalid Access Token, check Refresh Token and call another api to get new Access Token (doing this in client side). After that, call api to get data again with new Access Token.
If Refresh Token is invalid, deleting its record in database and user must to login again to get new Refresh Token.
Does I understand Access and Refresh Token technique correctly? Please give me some advices. Thank in advance.
Of the 4 steps you listed, some look more or less correct while others do not. I will begin this answer by giving the premise for why refresh tokens were created and what is their main purpose.
Using the JWT pattern with only access tokens, there is a potential usability problem when the JWT token expires. Consider as an example a banking website. When a user logs in, he receives a JWT token with a certain expiry (typically stored under the exp key in the claims section of the token). If the token is given say a 5 minute expiry, then from a usability point of view, it means that the website would have to force the user to manually login every 5 minutes. Obviously, this is not the best user experience, because it means that a user who happens to be in the middle of some business process when the token expires might lose all that work. This is where refresh tokens step in to alleviate this problem.
Using the JWT pattern with refresh tokens means that the user receives both an access and a refresh token. A typical workflow here might be:
After login, return to user Access Token and Refresh Token (using same technique JWT for both). The receiver notes when the access token is set to expire (say 15 minutes).
As the expiry of the access token approaches (e.g. 10 minutes), the UI will send the refresh token to the backend to obtain a new access token (and refresh token). This could be done explicitly, e.g. on a website which displays a popup asking if the user wants to continue. Or it could be done in stealth mode, with a REST call being made under the hood to get the new access token.
For the edge case where the refresh token cannot be used to obtain a new access token, then the very next user action which requires authentication would fail. In this case, the user would have to redirected to the login page. But, as this case should generally be rare, it does not disqualify the refresh token pattern.
I would also point out that storing the access/refresh tokens in the database largely defeats the purpose of the JWT pattern. One major reason for using JWT is that it pushes the user session state out of the application and onto the user. By storing tokens in your database, you are totally making your user sessions very stateful, which has all sorts of potential drawbacks. Consider using the suggested workflow above to avoid doing this.
The way I see it, your refresh token needs to be stored and associated with the device and the user.
Example:
User Logs In in Device A
Call Login endpoint
Validate user is valid
If valid, generate a refresh token associated with the userid & device
id
store required data to your table or storage engine (user_sessions..etc)
user_id | device_id | refresh_token | expires_at
Return the payload with access_token, refresh_token , access_token_expires_at, refresh_token_expires_at
Front-end, store the payload
when consuming a resource, check the following
If refresh_token_expires_at > now then logs them out , show your session is timeout (or you can have a never expired refresh_token.. ex. refresh_token_expires_at can be 0)
if access_token_expires_at > now then call refresh token endpoint along with your payload.
on the refresh endpoint, validate the call and check the refresh token against the data stored.
if refresh token is valid for this user+device, generate a new access_token
return the access_token and its expires_at
If the refresh token is INvalid , return invalid
front end will log the user out.
** in any case, if a refresh token was compromised, it will be only for that particular device/user. A user can then deactivate or remove the device from their list. This action will invalidate the refresh_token on their next refresh call.
We have an existing REST API that is currently using JWT.
Client came with the requirement that users can only use 1 device at a time. For example, if user logs in from iOS device then logs in Android device, then the iOS device should be "forced" to logout.
Since we are using JWT, we are not keeping track of tokens, except a Token Blacklist when user click Log Out.
I researched on how to "force" log out the user and it seems we would need to keep track of the last token used by the user, then invalidate that once we detect a new log-in.
Is there no cleaner / alternative way to achieve above?
Here are the steps to implement your requirement:
step 1: save the timestamp of last user activity performed for password update or logout from all device
add a column lastSessionResetDate to store last password update date in your user table
While performing forget password/change password apis or at logout-from-all-device, update lastSessionResetDate
step 2: set lastSessionResetDate in JWT Claims, when generating JWT token
example of setting claim while token generation
//make a claims map (`Claim extends Map<String, Object>`)
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, user.getUserName());
claims.put(CLAIM_KEY_AUDIENCE, "web");
claims.put(CLAIM_KEY_CREATED, new Date());
claims.put(CLAIM_KEY_LAST_SESSION_RESET, user.lastSessionResetDate());
//set claims and build JWT
return Jwts.builder().setClaims(claims).setSubject(user.getUserName())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + yourTokenValidity))
.signWith(SignatureAlgorithm.HS512,"yourSecretEncodedBase64")
.compact();
You can get any of claim value at time of parsing JWT token.
Step 3: Time to validate user and session
After getting User entity by JWT, check if lastSessionResetDate is valid till time, else unauthorized the request.
You must be already fetching user from database, so you don't need to make any other query because our new added column lastSessionResetDate will be a part of the same query result. (You just have to add one if-else block).
Note: You can also get prompt value in response from users for log-me-out-from-other-devices
Hope you have got an idea, Happy Coding!!
Assuming you use JWTs as Bearer tokens, depending on your requirements, it might not be sufficient enough "to keep track of the last token used by the user, then invalidate that once we detect a new log-in.".
As Bearer tokens (independent on whether it's JWT or not) are send by the client, the client is also aware of the token, which allows the sender to also copy and paste tokens from one device to another (or issue requests including the same token from various devices).
Your requirements sound like a standard use case for proper session management that allows e.g. for user-agent binding.
I have been reading up on how CSRF Tokens are implemented to prevent CSRF attacks. The OWASP page (https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet) and various articles state that one can generate a random unique token either on a per page basis or a per session basis. (of which they recommend generating it once per sessions)
If only one token is generated per session, then wouldn't that mean that all form pages using tokens for that session will have to have the same token every time the page is loaded (whenever say it is refreshed)? But in most implementations I have seen each load for the form has a different random token.
How does it work? After every successful check at the server side, is the CSRF token present in the session invalidated?
I just wanted to know if I am understanding this right. I read many similar questions on Stackoverflow and other blogs but I am still confused.
Thanks !!
I haven't read the OWASP page, but I believe in this context a session starts when a visitor first arrives at a site, and continues until the session expires (whether through inactivity or other generally server-defined criteria) or the visitor closes their browser.
When a session is first started, there won't be a CSRF token present in the session, so the server will generate one, and store the token in its internal data. A session handle is returned to the browser, and when the visitor loads or reloads another page on the site, it returns the session handle to the server, the server finds it has a CSRF token already set, and uses the existing one, rather than creating a new one. Thus, you don't need to worry about the token being invalidated as long as you only create a new one only if there isn't one already present in the session.
Even if the CSRF secret is only generated once per session, it's possible for each form to get a different token, by salting and hashing the secret (similar to how passwords are salted and hashed) that's sent to the browser. On form submission, the server can verify the salted token against its secret (again, similar to how passwords are checked). That way, each form can get its own unique token, without the server needing to remember or invalidate anything other than the per-session secret.
I currently have forms on my page, which are there regardless of if a user is logged in or not. Once a user logs in, they are presented with one of these forms (which use CSRF).
The issue is that if this box is presented after the authentication, the CSRF tokens are invalidated. I have confirmed this by allowing myself to submit the form without authentication checks and $form->isValid() returns true whereas after login, it gives me false with the error of:
The CSRF token is invalid. Please try to resubmit the form.
I guess there are three solutions - stop Symfony from regenerating/invalidating the CSRF tokens on authentication, remove the CSRF tokens from these forms or generate my form after authentication (I'd rather avoid this, however). My current solution is to pass a new CSRF token back with the authentication and set forms token input value.
Additional: Does anyone know how to view all CSRF tokens that are currently assigned? The session doesn't seem to hold them.
The usual approach is that when someone logs in successfully, the whole page is refreshed, as potentially different data is going to be displayed, customised to the user.
However if you really don't want to do that, You said:
My current solution is to pass a new CSRF token back with the authentication and set forms token input value.
Just do that. There is nothing wrong with that.
Thanks to #MarkFox for pointing me to this CWE - although I knew it was through design, I'd hoped there would be a way for me to avoid going down the route I ended up.
For anyone interested, the CWE states:
Authenticating a user, or otherwise establishing a new user session, without invalidating any existing session identifier gives an attacker the opportunity to steal authenticated sessions.
For this reason, I recommend that you go down the path I ended up, sending the new request tokens via AJAX and place them into the relevant forms.
I had one doubt about CSRF prevention. A lot of sites say that CSRF can be prevented by using 'tokens' which are randomly generated per session.
Now my doubt is,
suppose i have a function like :
$.post("abcd.php",{'fbuid':userid,'code':'<?php echo md5($_SESSION['randcode']); ?>'}
now this md5 hash would obviously be visible to any hacker through the source code.He could simply open this page, generate a token, and keep the page open, so that the session doesn't get destroyed, and useanother tab or anything else , to start hacking,
No ?
Or is my idea of tokens incorrect ?
Thanks for your help :D
I think you are misunderstanding what needs to be done. To protect against CSRF you need to create a token and save it for that session. Then you need to append all your submits and AJAX calls with that token.
For another person to send you to a page on your website they would need to have access to the request with in the same session. It is true that one could parse the HTML and look for the token. But when they try to request a http call on your website they will have a new session created. The new session will have a new token which will not match the token that was passed.
Next you will ask what if you can copy the cookies and the session id as a result. This is not something that is protected. I can simply sit anybody's computer and copy all their cookies and I will then be logged in as them.
As kapep points out, you are confusing the two seperate issues of input validation and cross-site form posting. You must validate your inputs anyway, so the case of your malicious attacker using his own session token is already handled if you have sound input validation. CSRF protection is not there to protect the data, it is simply to ensure that only forms from your own application can post data back to that application. the CSRF protection is simply stopping other people being able to post data directly into your app from forms they put up on their own site.
One specific point to be aware of is that the token is visible to any javascript running on your page, so as soon as you have a cross-site scripting (XSS) vulnerability, your CSRF protection is defeated.
See Cross-site scripting and the prevention cheat sheet
You should use a per request token.
Generate a token and store it in the session.
Pass the token to the client.
Execute actions.
Destroy the token.
The token is safer and cannot be used more than one time.
I would define a stolen token as a token that is used by someone else, and not the one you have send the token to. If you send someone a token he can't really steal it from himself.
If you are concerned that a user can run a malicious script with his own token, your design seems to be broken. You can't prevent a user from sending data that you didn't indented to receive. It's your job to validate any data, the session token is just there to identify multiple requests by the same client.
It could be a security issue if you send that token over unsecured http. Then it could easily be stolen by monitoring the clients network.