PHP CSRF Protection.

In this tutorial, we are going to show you how to guard against CSRF in PHP.

Cross-site Request Forgery (CSRF) is a type of attack which involves tricking the user into performing an action that they didn’t intend on carrying out.

This could be something as simple as directing a user to a logout URL. It could also be something much more serious. For example, the attack may force the user to delete a source.

Example CSRF Attack.

Here is an example of a CSRF attack.

<!--Image HTML with src attribute set to a delete URL-->
<img src='delete.php?id=2324' />

If an attacker manages to trick a user into viewing the above “image”, then the resulting HTTP request from the user’s browser might delete an important resource.

The browser will attempt to load the URL in the image src. This will result in a HTTP request being sent by the user to delete.php.

CSRF prevention in PHP.

To guard against these kind of attacks, we can give the user a randomly generated CSRF token as soon as they login in. We can then add this CSRF token to our HTML forms and query string parameters.

Generating a secure token with PHP’s openssl_random_pseudo_bytes function is actually pretty simple.

<?php

//After the user's login has deemed to be successful.

//Generate a secure token using openssl_random_pseudo_bytes.
$myToken = bin2hex(openssl_random_pseudo_bytes(24));
//Store the token as a session variable.
$_SESSION['token'] = $myToken;

We can then add that token to our HTML forms (with a hidden field) like so.

<form action="process.php" method="post">
    <!--Hidden field containing our session token-->
    <input type="hidden" name="token" value="<?= $_SESSION['token']; ?>">
    <input type="text" name="email" placeholder="Your email address..."><br>
    <input type="submit" name="submit_form"> 
</form>

Or we can include the token as a query string parameter on important links.

<!--Protecting the logout URL against CSRF-->
<a href="logout.php?token=<?= $_SESSION['token']; ?>">Logout</a>

Then, when the user attempts to carry out an important action, we can validate the CSRF token by comparing the token we received from the form or link against the token that is stored in the user’s session.

<?php

//Always make sure that you start your sessions.
session_start();

//For backward compatibility with the hash_equals function.
//This function was released in PHP 5.6.0.
//It allows us to perform a timing attack safe string comparison.
if(!function_exists('hash_equals')) {
  function hash_equals($str1, $str2) {
    if(strlen($str1) != strlen($str2)) {
      return false;
    } else {
      $res = $str1 ^ $str2;
      $ret = 0;
      for($i = strlen($res) - 1; $i >= 0; $i--) $ret |= ord($res[$i]);
      return !$ret;
    }
  }
}

//Make sure that the token POST variable exists.
if(!isset($_POST['token'])){
    throw new Exception('No token found!');
}

//It exists, so compare the token we received against the 
//token that we have stored as a session variable.
if(hash_equals($_POST['token'], $_SESSION['token']) === false){
    throw new Exception('Token mismatch!');
}

//Token is OK - process the form and carry out the action.

As you can see, protecting against CSRF in PHP isn’t that difficult.

It just takes a little bit of added code!