Preventing CSRF in PHP

Posted on February 16, 2010

Cross site request forgery (CSRF) is where a malicious website will attempt to issue actions on another website without the user’s knowledge of it occuring.

Hypothetical Situation: You had just done some online banking and had ticked the ‘Remember me’ option when you logged in. The banking website easily allows you to transfer money to other people. While browsing the malicious site you see a link that seems to take you somewhere harmless but it actually sends you to www.my.bank/transfer?to=3740384342?amount=99999 . Because you ticked ‘remember me’ you are automatically logged in and the bank goes ahead and transfers the funds.

There are some obvious fundamental issues the bank could address such as checking with the user whether they want to send the funds and masking their URLs but the problem of the form still remains. Theres nothing from stopping anyone sending seemingly valid data to the URL the form submits to. This problem is known as Cross Site Request Forgery (CSRF) and it is a potential problem in every single dynamic website. While stealing money is an extreme example, CSRF could also be used to steal cookies from a website or post spammy comments on a blog without the user being aware that they are doing so.

Actions on websites are always associated with forms – logging in, transferring funds or posting a comment on the blog. Our solution to the CSRF problem is to generate a unique key when the user visits the page with the form on it. We set this key as a $_SESSION as well as a hidden input field in the form. When the form is submitted we will check the key stored in the session and the key posted by the form. If they are the same then we know the user willingly submitted the form. If they don’t match (or the $_SESSION key is empty) then we know something malicious is going on and we can safely redirect the user (and take no action).

session_start();

//if the form was submitted..
if(isset($_POST['submit']))
{

//check the tokens
if($_SESSION['csrfToken'] == $_POST['csrfToken'])
{
echo 'form is good';
}
else
{
header("Location: www.query7.com");
}

}
else
{

//generate the token
$token = md5(uniqid(rand(), TRUE));

//set the token as a session
$_SESSION['csrfToken'] = $token;


//our form with the hidden field
$form = '
<form method="POST" action="">
Account: <input type="text" name="account" /><br />
Amount: <input type="text" name="amount" /><br />
<input type="hidden" name="csrfToken" value="' . $token . '" />
<input type="submit" value="Send Money" name="submit" />

</form>';

echo $form;


}

We can add an additional level of security by making sure the user was fowarded from the page that contains the form.


//after checking of the tokens match

if(!$_SERVER['HTTP_REFERER'] == 'http://yoursite.com/location/of/form.php')
{

    header('Location: http://www.yoursite.com/tricked.php');
}

If you have any questions about CSRF or how to protect against it please ask them in the comments.

Leave a Reply

You must be logged in to post a comment.

Sourcebits

iPhone Development and Flex Development

Categories