CSRF protection and usability - php

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.

Related

Auto login security with PHP, how and why [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I know this question has been asked many times, but I have a little different spin to it. It's a two part question.
First, I am implementing an auto login and I'd like to know where any holes are in my workflow.
Second, I'm unclear as to how a hacker would use the information they stole to compromise a system.
The first part, here is my workflow, what parts are vulnerable. Here are a few things that may, or may not, be helpful to know, I'm using PHP 5.6.21. I will be using their new hash API that uses the latest hashing technology and automatically salts it. I'm using the MVC architecture, so pretty much everything, other than my front controller and personal assets, are outside the root of the web server.
A member logs in and selects the auto login checkbox
A cookie with a hash value, that matches a hash value in the members table, is written to their browser. The cookie is only accessible from the members directory on the server, which is outside the root directory. Also, the httpOnly attribute is set.
The next time the member logs in, there is a check to see if that cookie exists, if so use the hash value as a key to lookup the member in the member table.
If they are found, log them in. If the cookie was set, but no matching record was found, then it's possible the cookie was tampered with. In that case, create a new auto login hash in the database and delete the cookie in the members browser. Then, force them to log in manually. If they choose auto login again, it goes back to step 1, this time using the new hash.
So, its pretty straight forward and no user data is stored in the cookie, just a meaningless hash.
The second part of my question is more general. Let's say a hacker somehow compromises the auto login cookie and gets the has code. What can they do with it? How can they use that to compromise my system?
In the case of SQL Injection, I understand that a data dump may be possible and a hacker could get a read only view of your database. In which case having hashed and salted passwords is a must! They can run those hashes through their evil machines and possible get your login credentials.
I was reading some security paper on why it is important to take all these measures. It kept saying that if a hacker get access to your database... But, if a hacker gets access to your database (with write privileges), none of this matters because you're screwed no matter how strong your hashes are.
Thanks for your time!
There are two parts to this:
Making sure your sessions are secure. Read this for a primer on session security, with configuration examples. Spoiler: HTTPS is mandatory, make sure you're using a CSPRNG for session identifiers, and you're regenerating session IDs during privilege escalation (and/or regularly).
Safely implementing a cookie-based authentication token that will transparently re-authenticate the user when they access your website with a valid cookie but without an active session. I've previously written extensively abut side-channel-resistant persistent user authentication.
The second part is more interesting, because many people stop at the "generate a random token, store in a cookie, and then look it up in the database" step.
You're safe to assume that your heavily-optimized database query isn't comparing strings in constant-time, like you need to be in order to prevent timing attacks.
The solution is to break your token into two pieces: One for the database lookup (timing-leaky), and the other for comparing in constant-time.
The first piece (called the selector) is stored/indexed in your database table.
The second (called the verifier) piece is stored in plaintext in your user's cookie, but is hashed (e.g. SHA256) in the database.
After retrieving the token based on the selector, you compare the hash of the verifier provided by the user with the stored hash, using hash_equals().
Each long-term authentication token should only be used once. Afterwards, a replacement should be issued to the end user. Logging out should clear the cookie, while the session should expire when the browser is closed.
This is only secure if you're using HTTPS with HSTS, and your cookies are set to HTTPS-only! (This means secure and httpOnly are both set to true.)
You can see an example of this system in action here and here.
When a user is required to enter a username and password every time he wishes to login, the only information stored client side is a cookie containing the session id. Now this is prone to session hijacking, which works on the principle that being client side, a user has full control of the session id sent to the server and in a non-secure environment, this is transmitted via HTTP (w/o any encryption). If a malicious user managed to get a hold of another user's session id, he could "hijack" that user's session.
There are ways to prevent or discourage session hijacking. One method is to whitelist the IP Address and/or browser information in the session. If the session conflicts with the "whitelisted" information, the session has been compromised.
Using your "auto-login" logic, your server only requires a single hashed string to gain access to a user's account. Since this hash is stored client-side, a user can "submit" any string they want in place of the hash. If the connection is not secured (HTTPS), and the network the user on is not necessarily trusted, a malicious user can intercept the hash string. They could theoretically use that single hash string to login as the user.
At this point, your hash string, no matter how strong the algorithm was to generate it, is simply the new "username/password".
Most of the holes in the security depend on an insecure connection. If the connection is well-secured with a modern cypher-suite, I don't believe you'd be prone to the same issues.
Assuming that all transport is secure (via HTTPS), the only reliable vector for an attacker would be stealing the cookie from a user's browser either physically or through some kind of browser exploit.
Some extra steps in regards to the auto login cookie could be taken such as:
Storing an IP and locking that auto login token to a particular IP or a range around that IP, anything else could be deemed as suspicious activity and you could terminate the auto login key
Storing the user agent and again, if it differs significantly (such as different browser) then it also could be unauthorised access and the auto login token should be revoked
In those cases it would be a good idea to notify the user that this has happened via email or another reliable option you have at your disposal.
Here's a idea to protect against the situation in the second question: store the hash in a location other than the database. We have an app that stores authentication tokens in Memcached - a server-side key-value store in RAM.
https://memcached.org/
We use a similar algorithm that you use here, except that we do a Memcached lookup instead of a database lookup.
Memcached has the additional advantage that you can set a timeout on how long a token is valid - allowing you to easily implement something where you log off after 30 minutes.

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.

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...

$_session security

Currently I autheticatic user sessions by matching a key in the session to the same key in a MySQl database. I regenerate the session with a random number that is MD5 protected on every page load. I am aware that sessions are not inherently secure and I'm looking for further security options that can be attached to this method in a speedy manner.
Any Ideas?
Since the session data is stored on the server side and the session ID is used to associate a client’s request with a certain session, it’s the session ID that needs to be protected. And the only viable measure to protect that session ID is to encrypt the connection between the client and server using TLS/SSL.
So you can use sessions as long as the data transfer between client and use is secured. Additionally, you can fix the PHP session to the TLS/SSL session so that the PHP session is only usable within that specific TLS/SSL session.
You're already jumping through hoops which do nothing to enhance the security, and potentially compromise the functionality of your site.
I autheticatic [sic] user sessions by matching a key in the session to the same key in a MySQl database
Even leaving aside the spelling mistakes, this is nonsense. Do you mean you authenticate requests by this method? If so, it's still not helping your security. You've already authenticated the request by de-referencing the session. Whether the request is authorized is completely different - if you need to authenticate the user then you should flag this in the session data.
It sounds like you're trying to prevent a CSRF, but getting this all mixed up with whether you're authenticating a user, a session or a request.
I regenerate the session...on every page load
Again, this is semantic nonsense. You can't "regenerate the session". Do you mean you create a new sessionId? If so then all you are achieving is creating errors when users try to open a second window or use the back button. It provides very little CSRF protection.
is MD5 protected
Just using random cryptographic functions doesn't make your application secure. It doesn't matter what the mapping between the real data and a surrogate identifier is, on its own it provides no protection against MITM.
Either you've done a very bad job describing your current security measures, or you've written lots of code which serves no useful purpose.
Go and read a lot of Stefan Esser's and/or Chriss Schiflet's stuff.

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