We built a SaaS platform that allows users to create a message and share it across any advertising medium via an pre-determined alphanumeric code.
All of our forms use a CSRF token, but I noticed a problem. If a user is editing their message, then takes a break, and comes back to finish editing and hits save, they are faced with a 'CSRF mismatch' warning message. This is a critical problem, as they have to refresh the page, losing their changes, in order to regenerate a valid token.
What's happening is that the initial CSRF token is generated based on static params such as filename and php_uname(), but it is also being generated via session_id(). So if the session_id changes, a new CSRF token is generated and obviously does not match the old token resulting in the warning message.
It would be easy enough to disable CSRF protection on forms that require user authentication, but we have a few forms that do not require authentication, such as user login. Granted it's not a big deal to show the 'CSRF mismatch' on a user login, but it does look wonky to users who have no idea what that means, and end up going 'screw this' and leaving the site.
So I am looking for an alternative solution, to maintain some semblance of form protection, without generating a worthless warning message.
Two potential solutions...
1) Instead of generating a CSRF based off session_id, generate it based off some cookie value, this will allow graceful cross session handling.
2) Drop CSRF altogether. Instead, validate the REFERER of all form posts, and if they match the parent domain, then proceed. This will ensure other sites are not posting to our forms (the primary purpose of CSRF). We actually use this solution for our ajax requests.
We are leaning towards option #2 but wanted to check to see if there were any other graceful solutions to consider.
I ended up going with option #1 (Cookie solution), and treat the CSRF seeding just like a persistent login cookie. If the seed isn't available in current session, check cookie, if exists, populate current session. Seems to be working well and transcends stale sessions.
Related
I am implementing CSRF token in my website on every post method.
But when i am accessing my webpages in different tabs then token gets change on both pages and token mismatches.
My token is stored in DOM and i am matching token using SESSION.
How to solve this.?
i change the token on every successful request
Yeah this is why we don't invalidate the token on every successful request. That doesn't just break multi-tab browsing, it also means you can't do stuff like hit the back button then submit.
“Invalidate token on every request” is the kind of bogus security recommendation you get from pentest reports where the tester hasn't found much that's really vulnerable. It's a trade-off as always whether you do, but the usability downside almost always outweighs the minimal security benefit.
You only really need to invalidate the CSRF token (along with the session token) on a privilege level change, most notably on login. This mitigates session fixation attacks, by preventing an attacker who knows the session and CSRF tokens prior to login from exploiting those tokens after you've logged in.
You can achieve this easily:
In the server side, store the CSRF tokens in session like this:
$_SESSION['csrf_tokens']['form1'] = //code to generate csrf token
While validating the token on form submit, you can check,
$_SESSION['csrf_tokens']['form1'] === $_POST['csrf_token']
Please post an example code, unless you are using ajax (which I wouldn't recommend for CSRF tokens the code shouldn't change in both tabs if you open a new tab). Also, I disagree with bobince, you are doing the right thing to implement this measure as once you have the logic in place you can easily and effortlessly use it in all your forms. The best way to implement this is to just have each token expire after a certain amount of time.
bobince: CSRF tokes are used to prevent CSRF attacks not session fixation attacks, both are different the former prevents scripts from executing actions on behalf of the user whereas the latter is an attack in which a malicious user impersonates a normal user by guessing or stealing their session id.
Generate two values – one random key (f.e. via uniqid), and a random token.
You generate both every time a form is rendered – and put them both into hidden fields. And you save the token into the session using the random key. Then when the form data is received, you check if the token send is in the session under the key send. (And if so, you delete the entry with this key after processing the form of course.)
Anything else (f.e. expiration time of tokens, binding of tokens to a certain form type out of several) you implement the same as you would before.
is unnecessarily and unsafe like this why you dont create a token based on session with openssl_random_pseudo_bytes() ,which will produce a safe token, and check if is correct or not or you can use also to expire after 2-5 min.also you can check on owasp about tokens on dom,can be easy spoffed !!!
I run into a problem. Following OWASP cheatsheet, I implemented a one-time-use CSRF token system in PHP (basically copy&paste from OWASP). Each form or link (link that generate some action) will create its own CSRF token, once it's used, it will be deleted. Application is a website, so multiples tabs could be opened at the same time.
The problem is that each time it load a page, it create a new CSRF token (even if you just hit reload and not send the form). For example, in the admin panel, there's a list of the items, each item could be deleted with a link which have a CSRF token (same CSRF token for all links), but if you reload the page, a new csrf is generated.
At the end of the day, I ended with more un-used tokens than I wanted to. This is a problem in certain servers.
TLDR; I generate a token per request. I deleted all used token (except for ajax request tokens, that after an hour I delete then). My problem is with UNUSED tokens, at the end of the day, there're too many of them.
There is no (practical) way of knowing if your user will still use the token or not. Thus, you will have to automatically delete (and thus invalidate) them after x hours, for example by using a cronjob.
Just a suggestion: Are you sure that you really need one-time tokens? OWASP themselves say that it is not absolutely necessary for security: "In general, developers need only generate this token once for the current session. [...]) (https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet).
Keep in mind that one-time tokens also make tabbed browsing or using the browser history a huge pain.
I have read many articles about CSRF protection (this is a good one) and various questions here on SO, but none of them seem to be informative enough to answer my question.
I am developing my own CMS and I want to secure my login and comment forms. I am going to allow anonymous users to comment on my website.
All of the forms on my website are secured using tokens. I already know about that approach, but the problem is that it needs an active session (that is, after the user logs in). The problem with the login and comment forms is that they are accessible to just about anyone and do not require you to log in - what would be the best protection against CSRF in this case?
On the link above, I read that it could be possible to create a "pre-session" when the user tries to log in and then proceed to the usual anti-CSRF methods (like assigning a token to the user's session), but I have no insight on how to achieve this.
The referrer header is a weak solution, so I guess I shouldn't bother. The Origin header, is, as far as I have tested, only supported in Google Chrome. What about custom headers? XMLHTTPRequest seems like a possibility, however, I have spent literally more than three hours on Google looking up some information about how should one implement such a security measure on their website. But even if I could use a custom header, doesn't it make it useless since the HTTP headers can be faked completely?
So, the question: how should I protect my login and comment forms against CSRF?
Edit: here's some additional information from the link that I provided above:
We recommend strict Referer validation to protect against login CSRF
because login forms typically submit over HTTPS, where the Referer
header is reliably present for legitimate requests. If a login request
lacks a Referer header, the site should reject the request to defend
against malicious suppression.
and
Secret validation tokens can defend against login CSRF, but developers
often forget to implement the defense because, before login, there is
no session to which to bind the CSRF token. To use secret validation
tokens to protect against login CSRF, the site must first create a
“presession,” implement token-based CSRF protection, and then
transition to a real session after successful authentication.
I just cannot put an end to this argument after reading the above quotes. One of them mentions using the referrer header, but I'm not quite sure whether it really adds much to the security of the webapp.
Edit 2: What about using CAPTCHAs?
The CSRF problem relates to someone using logged in user credentials to submit something. This is highly problematic as a malicious site can do stuff as anyone who's just browsed into your site. If you're talking about forms that can be used as anonymous, without logging in, there is lot less CSRF risk as there is considerably less to gain from posting to the form from another site - as anyone can do it directly also with same permissions.
So I don't get why protecting against CSRF for non-logged-in forms is needed.
If you do want this, a pre-session token could be technically similar to real session, but just a more light-weight one. It wouldn't really contain anything else than a generated token.
EDIT: about using the $_SESSION provided by PHP for the pre-session token, that's PHPs standard session mechanism. If you want to use that, then yes, that's about it.
However you're not forced to do it that way, and I personally wouldn't do it like that as it consumes server memory for all visitors, and that's not really needed. For a more efficient mechanism, basically you need a) a cookie identifying the user and b) something stored on the server side telling that the cookie is valid (and if needed, who is it valid for, meaning the ip). For a more light-weighted approach you can just create a token, store it in a cookie, and generate something matching that token in the form as hidden field, and match those on submit (like explained by Devesh). The latter would prevent submit of forms from another site, the former would prevent even the case where a malicious site does a lookup on your site and tries to set any cookies to the end user, too. So three approaches that I can think of:
just prevent image requests from other sites - using POSTs prevents this
prevent form submits from another site - form hidden field matching a cookie prevents this
prevent form submits from another site that do pre-lookup on your site - this would need IP verification, something stored on the server side, like ip in the database matched to the cookie
EDIT2: On captchas, their main use case is to prevent automated (brute force) login attempts. They would fix the issue with CSRF requests on login forms, too, but are an overkill for that. For preventing brute force login attacks they might be needed in some cases, although something more user friendly might be in order to not degrade usability too much. Maybe something like KittenAuth :)
You cannot realy protect an anonymous form against CSRF. Simply because the other site can act as a regular user. I can just create a site that does a curl request to the anonymous form and store the cookies and tokens in variables. And then make a second request to post the form.
The script isnt realy forging a request, but is just posting automatically.
The point of CSRF is to prevent a script/person to perform actions on behalf of another user. So that would be me trying to post as you. To prevent that the session/cookie with token approach is a good a solution. Because I have no way to get your session and token, unless your site is flawed in other areas. I would suggest to read the OWASP guidelines to get some idea on what you should be on the lookout for.
Another thing you should always do is make sure "actions" are always with POST request so I cannot simple put on image on your forum that links to 'http://www.yoursite.com/delete.php?id=10'. If you allow GET request and you open the page that contains this image, I would have forged a request. If you only allow POST it would have no result.
I think you can tackle the CSRF kind of problem by combining the hidden field added to your form and at the same time add the same value in the cookies and attach with the user response. When user post back the form try to match the hidden field value and the cookie value coming from the request , if both are matching you are good to go...
he CSRF problem relates to someone using logged in user credentials to submit something. This is highly problematic as a malicious site can do stuff as anyone who's just browsed into your site. If you're talking about forms that can be used as anonymous, without logging in, there is lot less CSRF risk as there is considerably less to gain from posting to the form from another site - as anyone can do it directly also with same permissions.
So I don't get why protecting against CSRF for non-logged-in forms is needed.
If you do want this, a pre-session token could be technically similar to real session, but just a more light-weight one. It wouldn't really contain anything else than a generated token.
In a attempt to make the current application I'm developing more secure, I've been reading about CSRF tokens and also Nonce.
My question simply is, Are CSRF tokens and Nonce the same thing? from what I could gather so far is that both these methods have different techniques to accomplish the same goal, or am I misunderstanding something?
If they are different, could you be nice enough to provide some example code or point me to some links where i can learn more about how to implementing nonces in PHP apps.
Thanks!
No, they're not the same.
Nonces prevent replay attacks (prevent eavesdropper from storing signed request and re-submitting it later, e.g. if Alice sends "Pay Bob $100", you don't want somebody to re-send that 100 times).
CSRF tokens patch HTML-specific weakness in authentication of users' action, where 3rd party website can submit forms with credentials of user viewing the site (e.g. JavaScript on evil.example.com submitting form to facebook.com using your browser, authenticated as you).
CSRF tokens need to be secret, otherwise attacker would have the missing piece required to forge a request.
Nonces don't have to be secret if they're signed with requester's secret (as long as attacker cannot replace one nonce with another).
You can allow replay of requests with CSRF tokens and still be secured against CSRF (you're interested whether that was intentional action by the user, but may not necessarily want to stop user from performing it many times).
In fact, that's very often useful property, e.g. allows users to use Back button and re-submit forms with corrected values. If you implement CSRF protection with Nonce-like mechanism, you'll get false alarms when users refresh submitted pages.
An easy way to prevent CSRF without Nonces is to put session ID in a hidden from field (not a value stored in the session, but ID of the session itself, the same that you store in the cookie [session_id() in PHP]). When the form is submitted check that form's session ID matches ID in the cookie. That is enough for CSRF, since attacker cannot know value of the cookie (CSRF only allows attackers to blindly send cookies).
Nonce is usually some random string that is added to request just to change in unpredictable way the data, which is used to calculate the signature. So nonce usually is not used by any server-side business logic.
While CSRF-token is stored somewhere on server, passed to the client and need to be returned back to the server to compare. And if matches - then OK.
So in your case the better will be to save csrf token once in a session variable like
$_SESSION['csrf_token'] = bin2hex(random_bytes(16));
and use it unchanged during the session life in all forms you have in your application.
(If you don't have random_bytes(), use random_compat to polyfill it.)
It's sort of the same thing. A "nonce" is just a one-time password itself. It can serve as cryptographic salt, but basically is just a random value. See WP:Nonce
But to sum it up, a nonce is often used as CSRF token. It's an implementation detail. The difference to other use cases is that it later gets asserted.
CSRF having some limitation.
in case if you have requirement where you want to open any page or link in new tab then CSRF won't allow. existing token will allow to open page in new tab for 5 times only.
when you will try to open 6th time it will create the new token which will not match with "server side = client side token". earlier token will expire and new token(NONCE) will create, in that case you will get 404 or 405 error.
So, I have a form, to make it a little more secure and potentially help prevent CSRF attacks I want to add a random token value in a hidden field that value is also stored server side in my session data.
When should I issue a new token? Per form? Per page load where there is any form? Per session? I can render it invalid as soon as a form is successfully submitted but I'm wondering when to generate one.
I ask as if I issue it per form or per page do I not risk the chance of a duplicate token value overwriting the existing (valid) token if a user opens a separate window but submitting the first form (with the now overwritten value)?
The simplest way to prevent concurrency issues is to generate it only once per login. The chance of an attacker "guessing" your CSRF is about the same chance (or lower) as them stealing your PHPSESSID. You could also regenerate it whenever the user's access level changes, such as after they change their password or something.
If you want to be really thorough, you can generate and store an array of tokens, one for each form on the website. But if they can steal a CSRF token, they might as well have just stolen a session ID and done some real damage.
If you use one Token per session,
then you need a technique to check
duplicate submitted forms (when user
refreshes a page or click back
button).
If use it per form then if user opens
multiple pages then only the last
form will work.
I would prefer using array of
generated tokens per session, and
when a token is submitted it should be
removed. I read that this approach might be a
concern if you have high volume
traffic website.
I am not sure if you read this article before, but I think it is great resource about CSRF security:
http://shiflett.org/articles/cross-site-request-forgeries