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.
Related
I have used JWT before, but they were API that didn't need logout feature.
I need to implement logout feature for a API of an android app and SPA. When I looked it up I found that there are two ways to do it.
the easiest way is to delete the JWT Token from client side and call it a day.
The logic behind this is that since no session of any kind is maintained in server deleting the token in client side should be enough.
But it still leaves the possibility that, if the token falls in wrong hands they can still use it even after the user is no longer using the token.
Given if the app is well designed and uses HTTPS then chances of this happening is very low and can be minimized by keeping the valid time for the token short. But in my case the tokens are valid for 30 days.
the second option is to maintain a blacklist of tokens in server side
This solves the problem of the token still being usable even after user has logged out and stopped using it.
But it adds a complication of needing to run a cronjob to remove expired token form the blacklist table. Otherwise the table will eventually become ridiculously large.
It also kinda defeats the point of using JWT. Maintaining blacklist is very similar to maintaining session. We have to run an additional db query for every request. And it scales badly, as the no. of users grows the no. of token that needs to be blacklisted will also grow (this will be a bigger problem for API like mine with multiple front end apps and long validity period for the tokens).
Then I got an idea for third way.
Add jwt_secret row in user table that stores randomly generated string. Use it to sign the JWT Token then on every request use user id in the jwt payload to get the user form db(which is not an extra query, we have to do this anyway) and validate the token signature using jwt_secret of the user. When the user logs out we change the jwt_secret which makes all token out there useless.
At first I thought this was a great solution only to realize that in this setup if user logs out of one device or browser he/she gets logged out of all devices.
So is there a another option? Or a way to modify any of above approach to solve the problem. Or am I over thinking this and one of the above option should be used?
For logging out, which as you pointed out is a user initiated action, I don't think you need to do anything extra. If the user somehow did not delete his JWT, then so be it. He wouldn't be getting any extra access over to what he is already entitled.
However, your question seems to hint on the problem of how to know that a JWT is valid. Again, as you pointed out, if a JWT somehow fell into the wrong hands, then there may be no avoiding this. But, with each request you would typically be doing several types of validation against that JWT, e.g.
checking the claims of the JWT, such as the token expiry date
assuming the claims pass, then checking that user's ID against your database table to make sure the account is active, has not been suspended, etc.
My point here is that if you need to keep track on the server side that a logout has happened, you might need to persist this to a database. But, I don't think you would need this.
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 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.
Is it a good practice to save the csrf token in a cookie or is it better to use a hidden field in a form? Also is it good to regenerate csrf token every user request like what captchas are doing?
Thanks
It is best to include it in the form. The idea behind a CSRF token is that it is not passed passively (e.g. if a malicious user is able to trick the browser into accessing some URL that does something nasty). Cookies are passed passively.
The best explaination to this question can be found on OWASP website at OWASP CSRF Prevention Cheat Sheet page.
Firstly, using cookie for a CSRF token can not help much because all cookies, even the secret ones, will be submitted with every request. All authentication tokens will be submitted regardless of whether or not the end-user was tricked into submitting the request.
Secondly, the application can include hidden input parameter in the form with a common name such as "CSRFToken". The value of this token must be randomly generated such that it cannot be guessed by an attacker.
Furthermore, Challenge-Response is another defense option for CSRF. It can be implemented in following ways:
CAPTCHA
Re-Authentication (password)
One-time Token
The CSRF cookie is certainly open to attack but implementation safe as the session value will always be checked against a submitted token value either stored in the body or header of the request so I can't see a reason against. The double submit (http only cookie vs post data) or token synchronizer (session vs post data) patterns outlined on the OWASP website are good pratices and both use cookies.
Double submit as mentioned earlier moves the storage to the client so is considered stateless but either way two tokens for comparison, of which one always remains unknown to the attacker.
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.