I have recently been working with a way to stop CSRF attacks happening by using a token. To my understanding:
1) User logs in, set session cookie to logged in and generate CSRF token and save it to the session
2) User submits form (with token) and it should match the token in session
Hypothetically lets say I have page1.php which has a sql SELECT to get all the users account information and then within the same page I also have an ajax button to page2.php which changes the account information. Obviously I protect page2.php from CSRF (because this is a post request) but how do I protect against page1.php? If this page was to get called from a remote source by ajax or putting an iframe on an attackers website, surely this would print all of the victims account information?
If this is the case how come I can't seem to find anything on protecting all pages from CSRF attacks and I only find resources for protecting against CSRF attacks in ajax?
Suppose you have a page at http://application.com/mypage with some data and a CSRF token generated. Attacker creates http://attacker.com/attack, and when a valid user of application.com visits, makes a request (either via ajax or in an iframe, doesn't matter) to application.com in order to get hold of application data. Standard csrf.
The reason this won't work for the attacker is the same origin policy. When the victim user is on attacker.com, the request to application.com will be cross-domain. If it's an iframe, data will be displayed, but attacker.com will have no access to it, it will only be displayed for the user that could have a look on application.com anyway. If it's an ajax call, the same applies, javascript on attacker.com will have no access to the response, ensured by the browser.
For ajax calls, access to cross-domain responses can be explicitly enabled by the access-control-allow-origin and related response headers (CORS) sent by application.com in the response.
Note that despite being cross-domain, the call will still be made (preflight requests come into play in some cases, but let's not go into that now). It's only the response that will be inaccessible for the attacker, and that is enough to prevent csrf.
(Also as a sidenote, application.com should prevent being displayed in an iframe by for example sending an x-frame-options: sameorigin header to prevent clickjacking and similar attacks, but that was not the question.)
I am not sure I get the point but the token for CSRF should be a nonce, which changes on each call.
Regarding a call coming from some other place, the ajax request should also check that the call comes from a valid user with the correct entitlments as you would do with any "regular" call to the site.
If this is the case how come I can't seem to find anything on
protecting all pages from CSRF attacks and I only find resources for
protecting against CSRF attacks in ajax?
What you ussually do is to set a nonce in a hidden input of the forms you use in "regular" pages.
Hypothetically lets say the page has a sql select on it and does not
request post. Now if this page was to get called from a remote source
by ajax or putting an iframe on their website, surely this would print
all of the victims data?
Make sure that all requests that hand out "confidential" information (either as a webpage or as data for an ajax call (or whatever in the future) do validate that there is a valid session active. If it's not, make sure the server side portion does not hand out anything confidential but instead throw an error and make sure your ajax client understands it and does the right thing for normal users (like telling them they're not logged in and offering them to fix that).
If this is the case how come I can't seem to find anything on
protecting all pages from CSRF attacks and I only find resources for
protecting against CSRF attacks in ajax?
For regular post requests typically the form is output with a hidden input field that holds the CSRF token. That's all.
BUT do make sure the code processing the request validates that the hidden field is present and filled in with the right value.
Also make sure that any request that modifies things is CSRF protected. E.g. that delete button in a non-ajax context should be protected with sending and validation of the CSRF token (hence the button ends up as a form with a hidden field for the CSRF.
Related
I am trying to prevent CSRF in php in the following way:
A $_SESSION['token'] is generated at the start of each page. I already know that using $_COOKIES is completely wrong since they are send automatically for each request.
In each <form>, the following input: <input type="hidden" name="t" value="<?php echo '$_SESSION['token']; ?>"> is appended.
The $_SESSION['token']; is validated with the $_POST['t']
Now I have several small questions:
Is this a good way to prevent CSRF? If not please explain.
When another page is opened as well that sets the same $_SESSION variable, the previous (still open) page becomes invalid, how to prevent this?
For forms this method is clear, but how to handle normal links? Is it necessary to append the token to each link as well?
Thank you very much in advance.
Is this a good way to prevent CSRF?
Yes. What this does is to force the client to do a GET on the form before it can do a POST to your form handler. This prevents CSRF in modern browsers since browsers will prevent client-side Javascript to do an XHR GET request to a foreign domain, so a 3rd party cannot imitate your form on their site and successfully get a valid token for the submission.
When another page is opened as well that sets the same $_SESSION variable, the previous (still open) page becomes invalid, how to prevent this?
Allow several tokens to be valid at a time, keeping an array of valid tokens in the session. Alternatively, store no tokens at all and use a token signing scheme instead. I've dabbled in and explained that here. Alternative 2: just use a single token for the whole session, without invalidating tokens. (tip o' the hat to #SilverlightFox in the comments)
For forms this method is clear, but how to handle normal links? Is it necessary to append the token to each link as well?
No. You only need to protect POST requests since presumably only POST requests can alter sensitive data (wink wink nudge nudge, you're sticking to REST conventions, right?!) and XHR GET requests are already blocked browser-side.
CSRF attack occurs when a hacker tries to send a fake request from an authenticated user. normally this attack occurs
in online shops or banks.
Prevent csrf attack in php we can:
1 = Create a check login function:
If the login session is set that is true Ok, if not false and return to login page.
2 = create a random makeToken hash function with: base64_encode(md5(microtime())) and save it into session and create a input hidden type with token name and the value with name Token's function.
3 = Create a checkTocken function and check it, if it is equall to makeToken function, and after that use unset function to unset the session, and create a new one.
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.
Let's say that we use a CSRF token in our forms, but it happens that there is an unnoticed XSS hole on our site.
From what I uderstand, CSRF token protection is completely void in this case, because attacker can retreive it with XMLHttpRequest through XSS.
In such case, is there a way to enchant the CSRF protection in a way that it would survive the attack or should our site first have a secure anti-XSS protection before doing any king of CSRF at all?
Setting a new token upon every page request instead of token on login would deal with it? This brings up the problem of having more forms open at once and I don't like it.
Your site should have closed any XSS holes that you've found otherwise CSRF is useless. However it would be useful to add CSRF in parallel so that once all XSS bugs are fixed the site's csrf protection is working too.
Unfortunately there is no way to protect against CSRF if there are XSS holes because with an XSS hole an attacker can read your website and check for tokens (using javascript). So any way and anywhere you add a token, that token can be found and then screenscraped
However if you make sure that there are no XSS bugs on your important pages and then add CSRF protection, there are still security holes but the skill level needed to chain multiple bugs together is more difficult.
Short Answer: Origin header check is the only csrf protection mechanism which will hold it's ground even when there is XSS vulnerability.
These are the techniques that we use to prevent CSRF
Synchronizer Token
With the Synchronizer Token approach, the server embeds a dynamic hidden variable in an input form (Pay close attention, server has to have absolute control on the form generation so that it can generate a random string and embed in the form) and keep it in the session on server side --> verify on the form submit and invalidate it from the session as soon as it is used once. This will not work with Restful services powering a totally detached SPA(Single page applications) as Microservices has no access to the SPAs' form generation mechanism. When the form is submitted, the server can check and make sure that the hidden variable is present and that it is the correct value. See, this is being sent in the body (If we set this new token to cookie instead of form body, it defeats the whole thing, there will be no difference between this and sessionID).
If mybank.com is not sanitizing the form inputs (or in other words, if mybank.com is vulnerable to xss) hackers can overcome this csrf prevention method. https://rileykidd.com/2013/09/09/using-xss-to-csrf/
Double Submit Cookie
With the Double Submit Cookie approach, two cookies are sent back to the browser. One is the session id and the other is a random value (similar to the synchronizer token) Lets say sessionid and csrfid are those cookies.
There are 2 things to put this mechanism to work.
1) A feature built into the browser called the Same Origin Policy. This permits the script code to interact with other server endpoints only if those endpoints have the same origin (base URI) as the endpoint that delivered said script code. You might be wondering, “Whoa ! If one cookie isn’t secure on its own, how are two cookies going to be more secure?” Hang on, The key is in the next point
2) Having the second cookie (csrfid) included in subsequent requests in a custom header (let's say, X-XSRF-Token). It is up to your client script code to ensure that this is setup properly.
When you request the login page, two cookies are sent back by the server. The second cookie is used in a custom header for subsequent requests from the browser. The server checks for the existence of the custom header and checks the value against what was sent for that page.
Similar to the Synchronizer Token approach, an external site trying to spoof a page and trick you into submitting data to an active session, would fail as it would not be able to set the custom header for a site at a different URL.
Main actions
Server have to generate a random value csrftoken cookie and send it to browser when the session is established.
Client needs to access the cookie and set into custom header for every subsequent request
Server needs to verify the CUSTOM HEADER (just ignore csrfid (or whatever name you gave) cookie. Since it's a cookie, it will be there with every request anyway, just ignore it )
This technique is effective because all browsers implement the same origin policy. Only code from the website on which cookies are set can read the cookies from that site and set custom headers on requests to that site.
Open questions (did not get a chance to test this out yet): What about the httpOnly on second (csrf-token) cookie.. do we need to set it or not? .. If we set it, will Javascript be able to access it to set to each subsequent request. On the other hand, if Javascript is able to access it, then together with un-sanitized-form-negligence XSS vulnerability, XSS attack can expose the user's csrf-token. Then, if evil-guy is be able to fool users into visiting evil-site-which-has-a-csrf-form.com while having an active session in mybank.com, csrf attack can take place. Agreed, there are too many "if"s for the attack to take place, but still it's not secure.
By far, these methods are not that effective if there is XSS vulnerability. Let's take a look at the 3rd option
Origin header check
All browsers, including Internet Explorer 9 and later, send an Origin header in their requests. This header is not allowed to be set by Javascript, which gives you a high degree of confidence that the browser has the right information in that header. The server can then explicitly check that header and verify that the base URI matches the server’s. If the server is set to reject browser requests where the base URI in the Origin header does not match the expected value, then a third-party site trying to spoof the look of your page would be foiled as the Origin set in the browser would be different than what was expected.
This will not fail even if mybank.com has a XSS vulnerability. Catch Nada! If your users are using (very) older versions of browsers you might have other problems to solve anyway :)
Reference:
https://stormpath.com/blog/secure-single-page-app-problem
I read in more than one website about this method of protecting forms:
I add a hiddenfield:
<input type="hidden" name="token" value="<?php echo $token; ?>" />
the token is generated by:
$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;
I do understand that this code is virtually unbreakable due to it's randomness.
What I don't understand is: why they include the token in a hidden form field which can be viewed in the html source?
Could then a user save the form and copy the valid md5 token to a fake version of the form and submit it?
This is designed to prevent CSRF.
The point is not to stop Alice visiting Bob's website, and then using the token to do bad things.
It is to stop Charles' website from using JavaScript to make Alice's browser submit a form to Bob's website and do bad things (with Alice's credentials). (Charles won't have a copy of the token to put in the form).
It is a strategy to help prevent Cross-Site Request Forgery. It doesn't matter that the token is in the HTML source, because it's only used once. The attacker would have to know what it is up front in order to trick the user, and even then the user has to have already brought up the form by legitimate means first.
This technique is used to prevent Cross-Site Request Forgery attacks where a malicious site is able to forge authentic and legitimate on the behalf of a victim when the victim is visiting a prepared web page of the malicious site. As the victim’s browser will send any authentication credentials along with the forged request, the server can’t distinguish whether the request was intended by the victim or not.
A simple example are <img> elements that cause the browser to send GET requests, another example are <form> elements that are automatically generated and send by JavaScript and can cause POST requests.
To mitigate this threat, this random token is used as a secret that is only known to the server and the browser: the server generates the random token, stores it in the session and issues it to the browser in the response that it then sends back in subsequent requests. Doing so, the malicious site isn’t able to forge legitimate requests as it doesn’t know the random token and isn’t able to get it.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to know where a form came from?
I was looking through these forums today but I couldn't find a good enough answer to my question.
How can I stop forms being submitted to my server unless they are referred from my domain. I have realised that if somebody just copies my form HTML directly and pastes it into their own platform, the data from their form will parse through my files and do what the form is set to do on my site.
How can I prevent this from happening? I was thinking of checking if the referrer is from my domain, but from what I have researched this wont prevent this from happening. So how can I stop this from happening?
REFERER is easily spoofed but is easy to check against so as a primary barrier of defense it is not too bad. For more sophisticated prevention, you could generate a token when the page with the form in question is loaded, store it in the user session, save it on a hidden field of the form, and when the form is submitted it check it against the session value. That can also be circumvented if someone wants to, though, so depending on what your specific case is HTTPS would then be the last resort.
There are two types of attacks that you might be trying to defend against.
A third party tricks a user into performing an action on your site
This is where Alice logs into Bob's website, then visits an attacker's website, and the attackers website causes Alice's browser to make (for example) a "transfer money" request to Bob's site.
This is a CSRF attack and the standard defence is to include, in a hidden field, a token that also exists in the user's session.
The attacker cannot get the token to put in their form, so you know the form is on your site if the tokens match.
A user modifies the data in the form to submit some data they really shouldn't
For example, Alice changes the POST_ID of a comment before submitting an Edit request and thus edits someone else's post, or perhaps she changes the price of goods being ordered.
The defence for this is to validate the input. If an edit request comes in, then make sure the logged in user has permission to edit the post. If an order comes in, then only pay attention to the items ids and quantities, you can get the prices from your database. etc.
You can't rely on the referrer! It can be disabled.
But you can use a token which you write a) in a hidden input field and b) into the user's session.
And in your target script, you just check if they are equivalent!
You're trying to make sure that when a POST comes in to your application that it came from your server's FORM and not somewhere else. This is known as a Cross-Site Request Forgery (CSRF) attack.
Checking the referrer is problematic. First of all, the HTTP specification specifically allows for clients to not send referrer strings (for various privacy reasons). So, some of your clients may not include it. Second, referrer strings can be spoofed, where an attacker of sufficient skill can make them look like what they need to be in order to carry out a successful CSRF attack.
Using a CSRF validation token is a stronger approach and is the preferred method of mitigiation against CSRF attacks. You can read about why this is on the OWASP CSRF Cheat Sheet.
I will also point out that there is no reason why you cannot do both. A Defense-In-Depth (DiD) strategy is usually desirable, so that an attacker would need to defeat multiple, independent, defenses to execute a successful attack. You could implement a weak-referrer-checking approach (IF a referrer is provided by the client, make sure it is what it should be before acting on the request; if the referrer is not present, proceed as if it were present and correct) along with a CSRF validation token. That way, you check the referred information if the client provides it while still making use of the stronger validation token approach.