CSRF tips and solution - php

So, I have made a class that inserts automatically in all forms an: <input type="hidden" name="csrf" value="csrf_value_uniq_id" />
Now my problems is that, I set the key to expire after 5 minutes, but if you stay on the page or you go to eat when you come back and you submit the form the csrf keys won't match.
Now, I could set this to expire in 24hours but then I don't know if this will be as safe as is meant to do.I'm using it on post forms.
So what's the best solution how should I do this?

There are two possible ways an attacker can get a valid token:
he is able to guess/predict the token, or
he is able to obtain it from a client.
The former can be mitigated by using a proper random source with enough entropy. The latter can be established by securing the transmission (i.e. HTTPS) and by protecting against attacks like Cross-Site Scripting which can be used to obtain a user’s token.
If you do both, you can even make it never expire (or at the end of the user’s session).

A solution which does not require a server-side state and thereby a timeout is a signed token. You create a random value which you include into the form, then you sign that token with a secret only you know:
$secret = 'weufiwu93tu2b248hg24';
$token = uniqid('', true);
$signature = sha1($secret . ':' . $token);
You then embed the token and the signature into the form. Upon receiving the form again, you repeat the SHA1(secret:token) operation with the token from the form and compare the result to the signature from the form. If you have a well chosen, random secret and a robust hash, nobody will be able to sign the token but the one who knows the secret, hence you have proven that the token came from you.
In addition, you can include a timestamp in the form/signature to limit the validity of the token (make it longer than 5 minutes, but not long enough for the token to be usable forever), the user id to tie a token to a specific user, the expected form fields to protect against form field injection and whatever else you may want to check against. E.g.:
signature = SHA1(secret:token:timestamp:userid:[form_field_name[:...]])
For the above signature, you embed the signature, token, timestamp and obviously the form fields in the form; upon submission you check that the submitted timestamp is within a certain window, take the secret and userid from the server, recreate the signature with all those pieces and check it against the submitted signature.
Note that the above code is just an example, your token should use a better source of randomness and be longer and your hashing function should be something more robust like HMAC or bcrypt. This here is to convey the idea, not the implementation details.
An actual implementation can be found here: Kunststube\CSRFP. I put this together in response to this question, since I needed something like it anyway.

Related

PHP: Best way to create and store token for confirmation email (3 options)

I would like to create a token in PHP that can be used for a confirmation email as part of a password reset request.
When searching for this online I came across a post here where I found the following:
$token = md5(uniqid(mt_rand(), true));
Can someone tell me if this should be preferred here compared to the below and let me know what are the differences here ?
$token = bin2hex(openssl_random_pseudo_bytes(16));
OR
$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
Also, can you let me know which datatype should be used to store this in the db (using MySQL) ?
Many thanks in advance,
Mike
Do not use uniqid() as a random value in any security-critical code. This includes password resets — an attacker may be able to predict the value of uniqid(), permitting them to guess a security token and reset the password of any user on your system.
Instead, use bin2hex(openssl_random_pseudo_bytes(…)) (as seen in your question) to create a secure random token. The PHP manual specifically recommends this function for the purpose in a footnote to the entry on uniqid():
This function [that is, uniqid()] does not generate cryptographically secure tokens, in fact without being passed any additional parameters the return value is little different from microtime(). If you need to generate cryptographically secure tokens use openssl_random_pseudo_bytes().
As the result of bin2hex() is a printable string, you can store it in your database using any string type. VARCHAR should be your default choice here, and it's perfectly appropriate.
For maximum security, you may want to take one additional step. Instead of storing the token in the database, store a hash of the token (e.g, sha1($token)) in the database, then compare the hash of the value presented by the user with the stored hash. In essence, treat it like a password! This extra step will prevent an attacker that has the ability to read your database, but not to write to it, from gaining control over a user's account by reading their "forgot password" token from the database.

Protect APIs against CSRF?

I have a PHP API that is being used by my website using Ajax requests!
An example of the operations I may perform using that API is (log user in, change password, retrieve sensitive data)
So simply, how can I prevent CSRF when using this API? It looks like it's a bit complicated for me to understand how!
I use HTTPS connection to perform these operation.. but I do not think that's enough to secure the operations!
You will want to use a Synchronizer Token. I don't think I can put it any better than OWASP does:
In order to facilitate a "transparent but visible" CSRF solution, developers are encouraged to adopt the Synchronizer Token Pattern (http://www.corej2eepatterns.com/Design/PresoDesign.htm). The synchronizer token pattern requires the generating of random "challenge" tokens that are associated with the user's current session. These challenge tokens are then inserted within the HTML forms and links associated with sensitive server-side operations. When the user wishes to invoke these sensitive operations, the HTTP request should include this challenge token. It is then the responsibility of the server application to verify the existence and correctness of this token. By including a challenge token with each request, the developer has a strong control to verify that the user actually intended to submit the desired requests. Inclusion of a required security token in HTTP requests associated with sensitive business functions helps mitigate CSRF attacks as successful exploitation assumes the attacker knows the randomly generated token for the target victim's session. This is analogous to the attacker being able to guess the target victim's session identifier. The following synopsis describes a general approach to incorporate challenge tokens within the request.
When a Web application formulates a request (by generating a link or form that causes a request when submitted or clicked by the user), the application should include a hidden input parameter 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. Consider leveraging the java.security.SecureRandom class for Java applications to generate a sufficiently long random token. Alternative generation algorithms include the use of 256-bit BASE64 encoded hashes. Developers that choose this generation algorithm must make sure that there is randomness and uniqueness utilized in the data that is hashed to generate the random token.
<form action="/transfer.do" method="post">
<input type="hidden" name="CSRFToken" value="OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWEwYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZMGYwMGEwOA==">
…
</form>
In general, developers need only generate this token once for the current session. After initial generation of this token, the value is stored in the session and is utilized for each subsequent request until the session expires. When a request is issued by the end-user, the server-side component must verify the existence and validity of the token in the request as compared to the token found in the session. If the token was not found within the request or the value provided does not match the value within the session, then the request should be aborted, token should be reset and the event logged as a potential CSRF attack in progress.
To further enhance the security of this proposed design, consider randomizing the CSRF token parameter name and or value for each request. Implementing this approach results in the generation of per-request tokens as opposed to per-session tokens. Note, however, that this may result in usability concerns. For example, the "Back" button browser capability is often hindered as the previous page may contain a token that is no longer valid. Interaction with this previous page will result in a CSRF false positive security event at the server. Regardless of the approach taken, developers are encouraged to protect the CSRF token the same way they protect authenticated session identifiers, such as the use of SSLv3/TLS.

CSRF protection and usability

Between most strong CSRF protection, there is the form token protection. The question I have about this method, is about usability: if a user opens multiple page containing a form, which use the token, are generated multiple token, but only the last opened page can successful send the form, all the other will give error.
I thought 2 solutions:
Keep a unique token for all the duration of session.
Store all generated tokens in session.
But:
This is the more realistic solution, but is less safe.
This generate a large resource overhead, because a user could open many pages, and I must store all the generated token.
Therefore, how have you solved this question?
PS The website I'm developing, is practically a ecommerce in PHP and although the money transfer will be managed through an external provider (like paypal), I think right give a good safety to my service.
You don't need to store tokens in the database.
Instead, you should include the same token in a cookie; a cross-site attacker cannot read or set cookies.
As long as the you get the same token in the cookie as the POSTed form, you're safe.
For additional security, you can hash them with a keyed HMAC hash, and verify that hash to make sure that the token came from your server.
You can also make the tokens per-user.

Is this enough for CSRF protection?

Is this enough for CSRF protection:
A random string is generated, $_SESSION['hash'] stores it
A hidden value (in $_POST['thing']) in a form contains the random string
When the form is submitted, it checks if $_SESSION['hash'] equals $_POST['thing'], and continues if they match
One of my site's users keeps telling me that my site is vulnerable, but I can't tell if he's just trolling me. Is there anything else that I can do?
What I think you are missing is limiting token to small window of time.
You should have a look at Chris's CRSF-article. A quick summary:
a CSRF attack must include a valid token (anti-CSRF token) in order to perfectly mimic the form submission.
The validity of the token can also be limited to a small window of time, such as five minutes
If you use a token in all of your forms as I have suggested, you can eliminate CSRF from your list of concerns. While no safeguard can be considered absolute (an attacker can theoretically guess a valid token), this approach mitigates the majority of the risk. Until next month, be safe.
If it's unique to every user, then it should be enough. Even if it's the same for duration of user session, it's still OK, but I would suggest to re-generate it periodically.
Also you may want to use different tokens per each form. For example, if you have login form and comments form, it's better to use different tokens for them, but it's not 100% necessary.
Why do you assume that just because someone says your site is vulnerable, it has to do with CSRF attach? They are so many other possible vulnerabilities.
Maybe your web server outdated and vulnerable, maybe the php version is not the most recent one. Maybe the user was able to login to your server via ssh or telnet. Maybe the user was able to guess admin password.
Maybe to let people login by cookie and store login credentials in cookies.
There are just too many things other than CSRF that could be exploited. There is also a possibility that the user is wrong or does not know that he is talking about or maybe he just wants to make your nervous.
Each time they load the page, it changes IF it's not already set.
Well there is your problem. Once a token is retrieved all the actions can be easily performed further one. I usually implement the token to be valid for one single request and afterwards regenerate it.
from : http://en.wikipedia.org/wiki/Cross-site_request_forgery
you can additional decrease time of life of cookie
check the HTTP Referer header
and captcha - but not every user like it
however your acion with secret key is still better than nothing...

How do I provide more security for checking source of the request

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

Categories