Anti-CSRF Tokens in PHP

In this tutorial you’ll learn how to implement HTML anti-CSRF tokens in PHP.

Contents


What is CSRF?

CSRF attacks are a dangerous type of web attack against end-users performed through malicious links. Attackers can send CSRF links using emails, social media content, web pages, forums, blog comments, JavaScript and so on.

The link sends the user to a vulnerable website where the user is already logged in. The goal of this link is to make the user perform unwanted operations on the website.

CSRF attacks work only on vulnerable websites. So, if you are building a website, you must make sure to protect it.
In this tutorial you will learn how to implement the most common defense technique: anti-CSRF tokens.

(P.S. If you want to know more about CSRF, take a look at this detailed introduction from my professional Security course.)

How to defend against CSRF attacks

There are a few different ways to protect your website from CSRF attacks.
Including: HTML tokens, JavaScript tokens, Cookie tokens, and HTTP headers.

In this tutorial we are focusing on HTML anti-CSRF tokens, which are the most used defense strategy.

Here’s how anti-CSRF tokens work:

  • First, you need to identify the pages where users can send data from. For example, pages containing HTML forms.
  • In each of those pages, you need to create a random token. You need to save this token in the user’s Session, and you also need to send it together with the request data (for example, including it into the form as a hidden input).
  • When reading the request back, you must check that the request token and the Session token are equal.

This technique effectively stops CSRF attacks because CSRF links cannot include the correct token.

Now, let’s see how to implement this technique.

How to create anti-CSRF tokens

Let’s say that you have a website with registered users.

Users can change their email addresses from the email.php page, which contains the following HTML form:

<form>
  New email address: <input type="email" name="email_address"><br>
  <input type="submit" value="Send">
</form>

This simple form is vulnerable to CSRF attacks.

An attacker can forge a link to the email.php page with the email_address request parameter set, like this: “email_address=attacker@email.com”.

Assuming that the victim user is authenticated to the website, the link will redirect the user to the email.php page and change its email address to the one chosen by the attacker.

So, how can you protect this form with an anti-CSRF token?

First, in the email.php page where you create the form, you need to create a random token.

You can use the PHP random_bytes() function to generate a random number, and bin2hex() to turn that number into a string:

// Generate a random token.
$token = bin2hex(random_bytes(16));

Then, you need to save this token in the user’s Session and also as an input element of the HTML form:

// Start the Session.
session_start();
// Generate the token.
$token = bin2hex(random_bytes(16));
// Save the token in the user Session.
$_SESSION['CSRF token'] = $token;
// Add the token as form input.
echo 
'
<form>
  New email address: <input type="email" name="email_address"><br>
  <input type="hidden" name="csrf_token" value="' . htmlentities($token, ENT_QUOTES | ENT_HTML5, 'UTF-8') . '">
  <input type="submit" value="Send">
</form>
';

If you are exchanging data using JSON, you can include the token directly into the JSON packet.

How to verify anti-CSRF tokens

Everytime the user opens the form page, a new token is created and sent together with the email change request.

Before allowing the email change, you need to make sure that the Session token and the request token are the same.

Here’s how to do that:

// Start the Session.
session_start();
// Read the request variables.
$email_address = $_REQUEST['email_address'];
$csrf_token = $_REQUEST['csrf_token'];
// Check if the submitted token matches the one inside $_SESSION.
if ($csrf_token === $_SESSION['CSRF token']) {
	echo 'Token valid. Updating your data.<br>';
	updateUserEmail($email_address);
}
else {
	echo 'Token invalid. Operation not allowed.<br>';
}

This technique effectively protects your site from CSRF attacks., because attackers cannot forge links with the correct token (which is only known to the user’s browser and the web server).

Note: it is better to use strict comparison to check that the tokens are exactly the same.

Conclusion

In this tutorial you learned how to implement anti-CSRF tokens quickly.

Here are the key concepts to remember:

  • You need to protect pages that receive user’s data.
  • For each user request, you need to create a random token and save it in the user’s Session and include it in the request data.
  • Each user request must include the same token in the request string and in the user’s Session.

Do you have any questions? Leave a comment and I’ll get back to you.

When you code in PHP, do you still feel like a beginner?

Get more confident about your PHP skills with 5 learning strategies. Get the free PDF now.

Leave a Comment