Protecting against CSRF attacks
CSRF stands for Cross-Site Request Forgery. These kind of attacks are very dangerous and therefore you should take care of them. CSRF attacks are far more unpopular than XSS attacks and that's the primary reason why so many web applications are vulnerable to CSRF attacks.
Where lies the Trust?
Unlike XSS, CSRF attacks exploit the trust that a site has for a particular user. The site is the target of the attack, and the user is both the victim and an unknowing accomplice.
Because the victim sends the request (not the attacker), it can be very difficult to determine that the request represents CSRF attack. To be more specific, if you haven't taken specific steps to soften the risk of CSRF attacks, your applications are most likely vulnerable.
When developing an application, challenging tasks include authentication, identification, and authorization. You may think or feel your script is totally safe from attackers, but you can still be vulnerable to serious CSRF attacks, because it allows an attacker to bypass traditional safeguards.
Example type of CSRF attack
Let's say John Doe has written a guestbook script. He has administrator control panel which no one else can access. In the control panel, he can delete guestbook messages by simply clicking a "Delete" link as the following shows:
HTML Code:
Message 32 (<a href="delete.php?message_id=32">Delete</a>)<br />
Although we have secure login system, we still have dangerous CSRF vulnerabilities. What do you think if John's guestbook uses cookies and someone put the following link into the guestbook:
HTML Code:
<a href="admin/delete.php?message_id=1">Click to see my website John!!</a>
Well. If John now presses the link, he will be autologged with cookies and he gets the guestbook message 1 deleted. Pretty nasty, isn't it? Think what could happen if Ebay had dangerous CSRF vulnerabilities? Then anyone could anytime make you to buy a $9999 product and if you have luck you may not even notice that!
Use $_POST and $_GET instead of $_REQUEST
One thing that you should always to do is to use $_POST or $_GET instead of $_REQUEST. Using $_REQUEST unnecessarily increases your risk. So never use $_REQUEST as seen sometimes in people's scripts. If you have form field "name" and you are using $_REQUEST, anyone can send the "name" as GET data instead of POST data like you thought, so use $_POST when you need it and $_GET when you need it. Forget the $_REQUEST.
GET requests are easily made by simple anchor link. But don't think that using just POST makes you safe. POST requests can also be forged, so do not consider a strict use of $_POST to be sufficient protection. Even JavaScript can launch POST forgeries.
Protecting against CSRF attacks
There are a few steps you can take to mitigate the risk of CSRF attacks. Minor steps include using POST rather than GET in HTML forms that perform actions, using $_POST instead of $_REQUEST, and requiring verification for critical actions. If you have a form which will delete, modify or other way do something important, it's good to have a "Are you sure" -style confirmation.
The most important thing you can do is to try to force the use of your own forms. If a user sends a request that looks like it is the result of a form submission, doesn't it make sense to be a little suspicious if the user has not recently requested the form?
Here's a sample how to make sure POST has been submitted from YOUR form. We will be using tokens.
csrf_form.php
PHP Code:
<?php
session_start();
$token = md5(uniqid(rand(),true));
$_SESSION["token"] = $token;
echo <<<TEXT
<form method="post" action="csrf_test.php">
<input type="hidden" name="token" value="$token" />
Field: <input type="text" name="field" /><br />
<input type="submit" name="submit" value="Send" />
TEXT;?>
I'll explain the above code to you. In the second line we will start a session. Then we will create totally random token with uniqid() function, and after that we will hash it with md5(). After successful generation of random string we will insert it to our session and also in our form. Now if someone submits data using our form, he will also submit random string with it. We need to compare session token and form token before we take any action after form submission like this:
csrf_test.php
PHP Code:
<?php
session_start();
if (!isset($_POST["submit"]))
{
if (isset($_POST["token"]) && isset($_SESSION["token"]) && $_SESSION["token"] == $_POST["token"])
echo "We used our own form!";
else
echo "Don't try to cheat, we do not allow CSRF attacks!";
}
else
echo "Did you submit anything?";
?>
In the above code we ensure the data IS from our form. And if our form is only accessible through login screen, the data is safe. We have successfully defended against CSRF attacks.
CSRF attacks are very dangerous, and most applications that do not take specific steps to prevent CSRF attacks are vulnerable. Because the requests originate from the victim, it is possible for an attacker to target sites that only the victim can access, such as ones on a local network.
Final thoughs
One thing you can also do. Check the referrer header. If it is not present, or if it does not show the correct URL as the referrer, reject the submission. This has the advantage of being simple and sane, but the disadvantage that users who have told their browsers to omit the referrer header (out of concern for privacy) or lie about the referrer will have trouble. This strategy doesn't work if the form uses GET and the page can contain user-generated content with links.
If you use a token in all of your forms as I have suggested, you can eliminate CSRF attacks from your list of concerns.
If you liked this tutorial, please feel free to
register at our forums or
donate to keep quality tutorials coming. :B