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.
Related
my previous algorithm for my CSRF token is using password_hash(), and this is multi-tab and cross-tab friendly. My only problem is that, when I start profiling my codes using kCacheGrind, this function is eating most of the time which is Incl. = 87.1 & Self = 87.11, I am not an expert in profiling but KCacheGrind said that I should focus with a high Self time. So I searched here, I saw ThiefMaster's answer and said that;
You could simply use a token which is persistent for the current
session or even the user (e.g. a hash of the hash of the user's
password) and cannot be determined by a third party (using a hash of
the user's IP is bad for example).
Then you don't have to store possibly tons of generated tokens and
unless the session expires (which would probably require the user to
login again anyway) the user can use as many tabs as he wants.
Is using a persistent CSRF token safe/secure?
what do I need to do to make a persistent CSRF token safe/secure?
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.
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.
I have to implement login tokens in my Lithium (the PHP framework) based application. Two reasons:
I want to have a "remember me" function.
I also need a way of tracking logins on the server so that I can verify authenticated users on a node.js socket server like so:
User requests a page
Server returns a view with a session token somewhere in the HTML
The client side JS reads the token and send it to the node.js server in an attempt to establish a connection via web sockets.
The server receives the connect request and verifies the token sent to it with the PHP server.
Allows or denies a connection based on the result.
So this is a two part question, and it's just to verify that I'm not being an idiot because the security on this site is of higher priority than usual.
Is this a reasonable way of creating a login token?
<?php
// String::hash() generates a sha512 (by default) hash.
String::hash(time() . $user['username']);
?>
Is the web socket authentication system I proposed sane? Can you see any problems arising or any more efficient ways of doing it?
First, you should change the way your login token is generated. A hash of the current time concatenated with the username is far from being difficult to guess; which is a necessary condition for it to be secure. There are many ways to do this, what's crucial is that you use a good source of randomness. You could do:
list(,$token) = unpack('H*', openssl_random_pseudo_bytes(15, $safe));
$safe or die("unsafe source");
The overall method you're using is only secure if you the bearer token you're passing around is never sent unencrypted. This means every time it is transmitted it must be transmitted over SSL/TLS.
There's still the problem that you may accidentally send the token to the wrong place, so you must be extra careful if any destination is somehow dynamically generated (e.g. through some discovery protocol). The only way to avoid this problem would be to use cryptography.
You should look at the RequestToken class in Lithium: http://li3.me/docs/lithium/security/validation/RequestToken::check()#source
It handles CSRF protection, and uses cryptographically secure random tokens, based on principles similar to the above, but with an extra layer of protection with bcrypt, which can match any number of unique hashes.
You could use the uniqid() function to generate a unique ID, to increase your security, and then you can drop or use together with the time.
<?php
String::hash(time() . uniqid() . $user['username']);
?>
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