What is the best way to protect again CSRF attacks in PHP.
It has been recommend to use form tokens, so basically generate a random value and store it in a hidden field. Then also store this random value in the users session.
Finally in the form action, make sure the session and form token match.
if($_SESSION['token'] !== $_POST['token']) {
die("bad... spanking...");
}
Is there a better/easier way, as this requires a lot of code modification in my application (lots of forms and actions).
No. To protect against CSRF, you need to make sure you only trust credentials that are automatically appended to requests (like cookies) when you have reason to believe that the user actually submitted your form. The only way to do that is to have the form carry some kind of secret that the server uses to authorize processing.
If you use a framework or library to compose your form it might help by generating the random number, setting the cookie or session property, and adding the hidden input to your form but those three steps do need to happen.
Related
I have tried two approaches:
-- Using form_open : With this approach, I am able to add a field with CSRF Token in request header as well as in cookies. But the same CSRF Token is generated every time and hence not able to prevent the attack.
Also I need to know apart from adding Token on client-side, is there any need to check it at server-side or it is automatically done.
-- Using hidden input field with custom form tags : With this, I added a random token as the input field, still not able to avoid the attack.
For second approach, I need to know the changes we need to do in Security.php file and for this also if we have to do any server-side check or not.
The first approach is advised mainly because the CI code is well-tested, tried-and-true code. I assume the second method is something you intend to write yourself. If that's the case you are reinventing the wheel without good cause.
Using the CI code it is important to understand that the hash value of the token will not change unless you use the following in config.php
$config['csrf_regenerate'] = TRUE;
The other thing you need to know is that a new hash will be generated only when a POST request is made to the server. That's fine because the need for CSRF protection is only relevant for POST requests.
When making multiple GET requests, i.e. loading a <form> a number of times in succession, you will likely see the same hash value each time. But if you submit the form and then reload it you will see a new hash value.
Finally, you should know that the CSRF values are only checked for POST requests and are not checked for GET requests.
The hash value will be removed from $_POST after it is successfully validated.
All of the above is happens automatically if you use the $config setting shown in combination with form_open().
Hello all,
While taking my time in the bath I though of something interesting. In PHP, how do you tell if the users' forms submitted is valid and not fraud (i.e. some other form on some other site with action="http://mysite.com/sendData.php")? Because really, anyone can create a form that will try send and match $_POST variables in the real backend. How can I make sure that that script is legit (from my site and only my site) so I don't have some sort of cloning-site data-steal thing going on?
I have some ideas but not sure where to start
Generate a one-time key and store in hidden input field
Attempt (however possible) to grab the url on which the form is located (probably not possible)
Using some really complicated PHP goodies to determine where the data is sent (possible)
Any ideas? Thanks all!
Most of these attempts from hackers will be used by curl. It's easy to change the referring agent with curl. You can even set cookies with curl. But spoofing md5 hashed keys with a private salt and storing it in session data will stop most average hackers and bots. Keeping the keys stored in a database will add authentication.
There are few simple ways like:
Checking $_SERVER['HTTP_REFERER'] to ensure your host was the referring script
Adding hashing keys in the forms and checking them with the server session variable stored.
But all the above can be manipulated and spoofed in some way. So, you can use CSRF Validations. Here is a very good article on this.
Other additional techniques I have encountered are:
Adding time limits to forms and ensure they are submitted with in that time.
On every interaction with the form, send AJAX request to validate and reactive the form's timelimit.
HTML5 provides a new input type . The purpose of the element is to provide a secure way to authenticate users.The tag specifies a key-pair generator field in a form.
When the form is submitted, two keys are generated, one private and one public.
The private key is stored locally, and the public key is sent to the server. The public key could be used to generate a client certificate to authenticate the user in the future.
Keygen tag in HTML5
RFC
I've just setup a simple CSRF protection in my application. It creates a unique crumb which are validated against a session value upon submitting a form.
Unfortunately this means now that I can't keep multiple instances (tabs in the browser) of my application open simultaneously as the CSRF crumbs collide with each other.
Should I create an individual token for each actual form or use a mutual, shared crumb for all my forms?
What are common sense here?
You can do either. It depends on the level of security you want.
The OWASP Enterprise Security API (ESAPI) uses the single token per user session method. That is probably a pretty effective method assuming you have no XSS holes and you have reasonably short session timeouts. If you allow sessions to stay alive for days or weeks, then this is not a good approach.
Personally, I do not find it difficult to use a different token for each instance of each form. I store a structure in the user's session with key-value pairs. The key for each item is the ID of the form, the value is another structure that contain the token and an expiry date for that token. Typically I will only allow a token to live for 10-20 minutes, then it expires. For longer forms I may give it a long expiry time.
If you want to be able to support the same form in multiple browser tabs in the same session, then my method becomes a little trickery but could still be easily done by having unique form IDs.
The OWASP Cheat Sheet has the most definitive answers for this sort of thing. It discusses different approaches and balancing of security vs. usability.
In brief they recommend having a single token per (browser) session. In other words in your case the same token is shared among tabs. The cheat sheet also emphasizes that it is very important not to expose your site to cross site scripting vulnerabilities, as this subverts the per session CSRF token strategy.
As I know about CSRF, you can use
1) Random number and save it into session:
Add it to hidden input called hidden, then when you recive info you can check hidden field with the session value
2) Static config variable (like previous but no session)
The hidden field will contain this value (variable from config). When you validate, you will check the hidden posted and the config security key value
3) HTTP Referer
You can use http referrer to know where user come from then check it with the real domain(this method can attack if your website contain xss).
As I know you can use any solution :)
I don't run a mission critical web site so I'm not looking for an industrial strength solution. However I would like to protect against basic attacks such as someone mocking up a false page on the hard disk and attempting to gain unauthorized access. Are there any standard techniques to ensure that form submission is only accepted from legitimate uses?
A few techniques come close:
Produce a form key for every form. The key would relate to a database record, and something else unique about the page view (the userID, a cookie, etc.). A form cannot be posted if the form key does not match for that user/cookie. The key is used only once, preventing an automated tool from posting again using a stolen key (for that user).
The form key can also be a shared-secret hash: the PHP generating the form can hash the cookie and userID, for example, something you can verify when the form is posted.
You can add a captcha, requiring a user to verify.
You can also limit the number of posts from that user/cookie (throttling), which can prevent certain forms of automated abuse.
You can't guarantee that the form isn't posted from disk, but you can limit how easily it is automated.
You can't. There's no reliable way to distinguish between an HTTP request generated from a user on your page, or a malicious user with their own web-page.
Just use a proper password authentication approach, and no-one will be able to break anything unless they know the password (regardless of where the HTTP requests are coming from). Once you have reliable server-side authentication, you don't need to waste time jumping through non-robust hoops worrying about this scenario.
You should not create a login-system yourself because it is difficult to get it right(security). You should NOT store the passwords(in any form whatsoever) of your users on your site(dangerous) => Take for example lifehacker.com which got compromised(my account too :(). You should use something like lightopenid(as stackoverflow also uses openid) for your authentication.
The remaining forms you have on your site should have the following protection(at least):
CSRF protection: This link explains thorougly what CSRF is and even more important how to protect against CSRF
Use http-only cookies: http-only sessions, http-only cookies
Protect against XSS using filter.
Use PDO prepared statement to protect youself against SQL-injection
i also recommend:
Save the IP of the computer that sends the form (to block it from the server if it.s annoying)
Use CAPTCHA when required, to avoid robots...
Send users to another page when the info is loaded, so the POST data won't be retrieved when you refresh the page.
Proper validation of form data is important to protect your form from hackers and spammers!
Strip unnecessary characters (extra space, tab, newline) from the
user input data (with the PHP trim() function)
Remove backslashes () from the user input data (with the PHP
stripslashes() function)
for more detail, you can refer to Form Validation
I am developing one PHP web application, I want to provide more security to application so that no one can easily break the functionality.
Brief explanation about my problem :
In one module there is one stage where I am checking the source of the request ( from where this request is coming from )
Currently, I am using HTTP_REFERRER variable ( available in php ). I am checking this variable value with one specific URL (e.g. http://www.example.com/test.php ). If exact match exist then only I am calling further actions.
I am bit confused with above approach, whether should i use HTTP_REFERRER or check with IP address( valid request if it is coming from any specific IP address )?
I also want to know better approaches for providing security.
Is anyone has idea then please share ?
Thanks in advance
Lesson #1 in web security:
NEVER trust user input. And when I say never, I mean never. ;) Including the HTTP_REFER var in PHP which is easily compromised with an http header (source: http://www.mustap.com/phpzone_post_62_how-to-bypass-the-referer-se)
A possible solution in checking the source is the using a form token (csrf protection): http://www.thespanner.co.uk/2007/04/12/one-time-form-tokens/ but isn't that safe either and is only possible with your own source.
A simple CSRF (cross-site request forgery) protection example: (Hence the simple. For a more safe/robust solution, refer to the answer of The Rook)
1) In your form page, create some kind of token and put in your session and in a hidden form field:
<?php
session_start();
$csrfToken = md5(uniqid(mt_rand(),true)); // Token generation updated, as suggested by The Rook. Thanks!
$_SESSION['csrfToken'] = $token;
?>
<form action="formHandler.php">
<input type="hidden" name="csrfKey" value="<?php echo $csrfToken ?>" />
</form>
2) In your form handler check if the token is valid.
<?php
session_start();
if($_POST['csrfKey'] != $_SESSION['csrfKey']) {
die("Unauthorized source!");
}
?>
Checking the HTTP_REFERRER for CSRF is a valid form of protection. Although it is trivial to spoof this HTTP header on your OWN BROWSER it is impossilbe to spoof it on another persons browser using CSRF because it breaks the rules.
According to the Department of Homeland Security I have found the most dangerous CSRF vulnerability ever found and is in the top 1,000 most dangerous vulnerabilities of all time. Motorola patched this flaw using a referer check, and its common to see this protection method on embedded network hardware because memory is scarce.
A more common and more secure method is to store a Cryptographic nonce inside a $_SESSION variable and check this for each sensitive request. An easy approach is to use POST for all sensitive requests (like changing your password) and make sure this Cryptographic nonce is valid for all posts in a php header file, if it isn't valid then unset($_POST);. This method works because although an attacker can force your browser into SENDING GET/POST requests he cannot view the RESPONSE, and there for cannot read this token needed to forge the request. This token can be obtained with XSS, so make sure you test your site for xss.
A good method for generating a csrf token is md5(uniqid(mt_rand(),true)); This should be enough entropy to stop CSRF. md5() is used to obscure how the salt is generated. Keep in mind that the current time is not a secret, the attacker knows exactly what time the CSRF request is produced and can narrow down when the session was created. You must assume that the attacker can make many guesses, and in practice this is simple to accomplish by writing a bunch of iframes to the page.
Treur got it right, but I still want to clarify a few things and provide you with some sources for reference material. As Treur said, NEVER ever trust user input data, that includes all headers sent by the browser.
What you are describing, is a typical Cross-Site Request Forgery attack. Checking the referrer header is not a valid protection against CSRF attacks, since according to the RFC2616 (Hyper Text Transfer Protocol 1.1), the referer header is optional and thus may be omitted by the browser at any time. If you are using SSL, then the referer header is always omitted by browsers. Secondly, it is a user defined value, and thus should not be trusted.
The recommended protection against CSRF attacks is to use the synchronized token pattern. This means that you should create a secret token which is embedded as a hidden field in your form. When the form is posted, you verify that the secret token is present and that it is valid. There are multiple strategies for creating security tokens. I'll describe one way for creating the tokens:
For each action in your application, create a unique action name for them. For example, "delete_user", "add_user" or "save_user_profile". Let's say that the form you described has the action name "foobar". Concatenate the action name with the user's session id and a secret value.
$stringValue = "foobar" . "secret value" . session_id();
To create the security token, create a hash of the concatenated string, you can use sha1 to create the hash. To decrease the risk of brute force attacks, use a larger key in the hash, for example, sha 512.
$secretToken = hash("sha5125", $stringValue);
Set this token in your form's hidden field. When the form is submitted, recreate the token and verify that it matches the one submitted in the form. This token is valid for one user session. One may argue, that there is a window of opportunity where an attacker can reuse the token as it is not regenerated at every request. However, with proper session management strategies, this shouldn't really be a concern.
Like I said, proper session management is necessary. This means that you shouldn't keep the sessions alive to long. Especially session fixation vulnerabilities will undo any CSRF protection measures, as the attacker is then in control of the user session and hence can "predict" the secret tokens.
Here are a couple of links that I recommend you read through:
OWASP: Cross-Site Request Forgery
OWASP: Cross-Site Request Forgery Prevention Cheat Sheet