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.
Related
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.
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 want to use a token to prevent CSRF attacks on my website (written with PHP). I've used it in forms and it works well. But logout link is not a form; It is only a hyperlink.
Is it secure if I put the token in the query string like this:
Logout
If it has any problem, what is your suggestions and solutions ?
Yes, if the CSRF token is 'unguessable' and validated: the approach is the same in both cases.
From Wikipedia's Cross-site Request Forgery - Prevention:
Web sites have various CSRF countermeasures available .. Requiring a secret, user-specific token in all form submissions and side-effect URLs prevents CSRF; the attacker's site cannot put the right token in its submissions.
It doesn't matter if the token is from a form value or a query string parameter1. An approach that prevents CSRF by including a token in forms is adaptable to (and valid for) hyperlinks2.
1 A MitM / proxy which can intercept a URL can just as easily intercept an HTML form. This is outside the scope of a standard CSRF attack or mitigiation of such. In such cases the CSRF token value is 'knowable' and system is not secure.
2 This assumes the token is a per-user (and time-sensitive) value. A simple HMAC hash of the Session ID should be sufficient in most cases.
I think one of main disadvantages of using CSRF-token in GET requests is possibility of incompetent user to easily disclose his token by copying a link with the token and paste it in some public content like a comment/post/etc... Also GET query parameters including CSRF-tokens usually logged by HTTP servers/proxies and it introduces another risk.
So I suggest you to implement CSRF-secure links using something like this:
<form name="logout" action="logout.php" method="post">
<input type="hidden" name="token" value="9ae328eea8a72172a2426131a6a41adb"/>
</form>
...
Logout
Others made some good points. I add this answer to augment theirs.
Always filter and validate your query string name/value pairs.
Given: You have a database and you want to create a link to help dynamically get (but not change) content that a user can link to. Example: news articles, case studies, public user profiles.
Requirement: Use a query string and deter CSRF by using a token.
One of the purposes of a CSRF token is to help verify that an incoming HTTP request was generated from a page served from your domain (regardless if sessions are in play. The session token is a separate beast). A CSRF token works best when you use more than one defense vector.
Comparison: Check that a submitted token matches one in session.
Time: You can specify a token is only good for a certain period into the future.
public function setFormToken()
{
$token = $this->cipher->getFormToken(); //Some hashing algorithm.
$this->formToken = $token; //In this example, used to insert the token into HTML.
$_SESSION['token'] = $token; //Save the token for comparison upon form submission / HTTP query string processing.
$_SESSION['tokenExpireTime'] = time() + (60 * FORM_TOKEN_EXPIRE_MINUTES); //This is just an abstract example.
return;
}
Then, in addition to comparing the submitted token to the one in session, verify that the submission period is still valid.
private function hasTokenTimeLeft()
{
if(isset($_SESSION['tokenExpireTime']) && (time() < $_SESSION['tokenExpireTime']))
{
return true;
}
throw new SecurityException("Token time has expired for this POST request.", 911);
}
Sessions: If a session has expired, or is non-existent, the a false HTTP request for your content should fail.
Request Info: With HTTP POST request, some attempt to check that the token, user agent, and IP match the one from the original HTTP GET request (which means storing it in session before responding to the GET request).
Query strings, as previously mentioned, can get cached. There is no problem with that if the data is supposed to be publicly available, anyway. Think about someone bookmarking a product on an e-commerce website.
What you have to ask yourself is "Should I be able to get to this content from anywhere, anytime, through this link with a query string?" If yes, do not use a true, backend, randomly generated, CSRF token. Imagine you are running for elected office by word of mouth and people sending links to their friends and family in email (ok, bad practice). In this case, you never want to set off a "trip wire" and cause a fail condition. If a fresh token always needs to be generated on the backend first, emailing your link around will not work.
If you should not be able to get to content from outside of your security context (i.e., before logging in), then you are free to take whatever measures are necessary to fortify your CSRF token strategy using query strings.
Your log out snippet is a perfect example.
Logout
No one from outside your security context should be able to use the logout feature. You want the "trip wire" if someone emails this link around! Obviously, sessions are integrated into this solution (the token has to be stored somewhere while the user uses the page). Session duration and logging out should be managed carefully, and I say you are doing a good job.
Encryption: The only thing you could do better is encrypt the hash/token for the query string, then decrypt it and verify it upon submission. Additionally, you can break the token up into pieces, mix the pieces up, and basically use some security by obscurity string techniques to make your CSRF token more resilient.
I am learning about how to prevent CSRF using anti-CSRF tokens. Essentially, the idea is to:-
1) generate a token eg Md5 or Sha1 then store this value in a session variable:-
$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;
2) All forms include this token value in a POST hidden field
<input type='hidden' name='token' value='$nonce_token' />
Eg what it would look like to user in source code:-
<input type='hidden' name='token' value='9ee66e4e63a06ee4b83a3edde4ecd587' />
3) Once form sent check POST hidden field token value matches token stored in session value
if($_POST['token']==$_SESSION['token']){...ok...}
However, this process seems a little flawed since by including the token value in a hidden POST field an attack can simply just look at the website source code to see the token and then just include this in a malicious generated POST form which my application would thus succeed once received as token value sent would match the token value in my session variable, since I essentially show the token value in my hidden field to the attacker.
Thus, my question is what is the best way around this, as a few ideas I had still seem little flawed:-
1) Using _GET instead but this still has flaws like _POST
2) Changing the token value after x minutes or each request but causes usability issues when going back in browser or fail when user filling in form and token value would become outdated compared to updated session token value as hidden token value would not have updated whilst user filling in a form.
3) Try encrypting hidden POST form token value then decrypting on sending POST but encrypting/decrypting an already hashed value seems complicated especially one way encrypted has values like MD5 etc?
Any ideas would be much appreciated.
What you need to do is make the hidden field the MD5 or SHA1 hash of the session ID with a salt. That way you compare the submitted value with the hash of the session ID plus salt and if they match it is valid. If an attacker can guess the token then they have already stolen the session id and would be pointless to do anymore protecting since login has already been hijacked. It's really as simple as that. Here is some great info per OWASP on how to prevent CSRF https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
However, this process seems a little flawed since by including the token value in a hidden POST field an attack can simply just look at the website source code
No they can't.
Alice runs a website. Bob visits the website. Mallory is attacking Bob's account.
Bob gets a nonce token when he visits Alice's website.
If Mallory visited the site, Mallory would get a different nonce (because Mallory would have a different session).
If Mallory generated a form with malicious data in it (on her website) and tricked Bob into submitting it, then the nonce Mallory put in the form would not match the nonce in Bob's session and the submission would be rejected.
Let's review the attack scenario:
You have a server at example.com and you use CSRF tokens in your forms.
Each CSRF token is unique, specific to a user and only valid for some time.
A malicious third party, Eve, tricks one of your users, Alice, to come to her site, attempting to mount a CSRF attack.
If Eve simply tricks Alice into submitting a form to your server without CSRF token, your server will reject it.
If Eve also has an account on your server and tries to get any token to submit with the form, this will fail because the token is not valid for Alice.
This leaves this scenario: Using Javascript, Eve fetches a form from your server as Alice, then submits this form back, including a valid token. I.e. Eve completely impersonates Alice for the entire process of a regular form submission using Javascript. This is prevented by the Same Origin Policy. Eve's Javascript won't be able to fetch information from your server, Alice's browser will prevent this as it violates the Same Origin Policy.
That is, assuming there are no security holes in the browser which allow Eve to circumvent that policy. This also means that you need to guard against XSS, i.e. against Eve being able to inject one of her scripts into your website, so regular visitors to your site will run Eve's scripts as part of your site, from the same origin.
As a bit of self-promotion, I've just implemented a signature based CSRF token library, which you may want to look at: Kunststube\CSRFP. I'd also like to solicit peer review and criticism of it, while I'm at it.
At first, you have to keep in mind, that you cannot prevent hackers from attacking your application, only you can make things harder.
The idea is come clearly when you thinking about what is the main goal of CSRF attacks, The CSRF is an attack that tricks the victim into loading a page that contains a malicious request. It is malicious in the sense that it inherits the identity and privileges of the victim to perform an undesired function on the victim's behalf, like change the victim's e-mail address, home address, or password, or purchase something. CSRF attacks generally target functions that cause a state change on the server but can also be used to access sensitive data.
So as above, attackers don't make attack directly to your web page, they need bridge, that's it they need a Victim, so they can use victim identity and privileges to execute actions.
when you said:
However, this process seems a little flawed since by including the token value in a
hidden POST field an attack can simply just look at the website source code
it's doesn't make sense, because attacker will not attack himself.
i hope this was help full.
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