How to Protect Your Website Using Anti-CSRF Tokens

Wed, 10 Jun 2020 - by Netsparker Security Team

Anti-CSRF tokens protect against cross-site request forgery attacks. This article explains the basics of anti-CSRF tokens, including how to generate and verify them. It then describes anti-CSRF protection for specific forms and each request. Finally, it examines issues on CSRF protection, such Ajax, login and non-persisted tokens.

How to Protect Your Website Using Anti-CSRF Tokens

Anti-CSRF tokens (or simply CSRF tokens) are unique values used in web applications to prevent Cross-Site Request Forgery attacks (CSRF/XSRF). CSRF attacks are client-side attacks that can be used to redirect users to a malicious website, steal sensitive information, or execute other actions within a user’s session. This article shows how to use CSRF tokens to protect your users against CSRF attacks and their consequences.

Protecting Your Website Using an Anti-CSRF Token

Anti-CSRF Token Basics

The basic principle behind anti-CSRF tokens (also known as synchronizer token patterns) is to provide the user browser with a piece of information (a token) and check if the web browser sends it back. The token must be unique and impossible to guess by a third party. The application must not proceed unless it verifies that piece of information. This way, only the original user can send requests within an authenticated session.

Let’s say you run a social media web application on To publish a message on their profile, the user fills an HTML form and clicks on the Submit button. 

<form action="/action.php" method="post">
  Subject: <input type="text" name="subject"/><br/>
  Content: <input type="text" name="content"/><br/>
  <input type="submit" value="Submit"/>

This causes the web browser to send a POST request:

POST /post.php HTTP/1.1


If the user is logged in and if the attacker knows the syntax of this request, the attacker could use a CSRF attack to publish an advertisement on the user’s profile:

<form action="/action.php" method="post">
  Subject: <input type="text" name="subject" value="Buy my product!"/>
  Content: <input type="text" name="content" value="To buy my product, visit this site:"/>
  <input type="submit" value="Submit"/>

As a result, the web browser sends the following POST request:

POST /post.php HTTP/1.1

subject=Buy my product!&content=To buy my product, visit this site:

If your site uses a simple anti-CSRF token, the web server sets that token in the session cookie of your web browser right after you log in. All the form submissions include a hidden field containing the token. This completely eliminates the CSRF vulnerability.

  Subject: <input type="text" name="subject"/><br/>
  Content: <input type="text" name="content"/><br/>
  <input type="submit" value="Submit"/>
  <input type="hidden" name="token" value="R6B7hoBQd0wfG5Y6qOXHPNm4b9WKsTq6Vy6Jssxb"/>

Then the server would check if every POST request looks like this:

POST /post.php HTTP/1.1

subject=I am feeling well&content=I just ate a cookie and it was delicious.&token=R6B7hoBQd0wfG5Y6qOXHPNm4b9WKsTq6Vy6Jssxb

If the attacker tries to perform a Cross-Site Request Forgery using a malicious site, they will not know the current token that is set in the cookie. Your server will not process a request without this token, so the attack fails.

How to Generate and Verify Tokens

When you generate and later verify your token, follow these principles to make sure that your anti-CSRF token cannot be guessed or otherwise abused:

  • Use a non-predictable, well-established random number generator with enough entropy.
  • Expire tokens after a short amount of time so that they cannot be reused.
  • Use safe ways to verify whether the received token is the same as the set token, for example, compare hashes.
  • Do not send CSRF tokens in HTTP GET requests, so that they are not directly available in the URL and they do not leak in the Referer header with other referrer information.

For example, in PHP you can generate a token as follows:

$_SESSION['token'] = bin2hex(random_bytes(24));

And verify the token as follows:

if (hash_equals($_SESSION['token'], $_POST['token'])) {
  // Action if token is valid
} else {
  // Action if token is invalid

Anti-CSRF Protection for Specific Forms

The anti-CSRF token described above is set upon login in the user session cookie and then verified by every form. In most cases, this protection is enough. However, some sites prefer to use a more secure approach. To achieve a good compromise between security and usability, you can generate separate tokens for each form.

To achieve this, generate a token but do not expose it directly to the user’s browser. Instead, hash the token with the filename of the form, for example:

hash_hmac('sha256', 'post.php', $_SESSION['internal_token'])

When you verify, compare the hashes. If the token is valid and the current form is valid, hashes will match.

Anti-CSRF Protection for Each Request

If you need a very high level of protection, you may use separate tokens for each request. This is simple to implement: all you need to do is invalidate the token after it is verified.

This approach has several drawbacks for users. For example, users who like to work in several tabs will not be able to do so. The back button will break the flow as well. Therefore, before considering this approach, make sure that it will not negatively impact the user experience. When creating per-request anti-CSRF tokens also consider server performance and use less resource-intensive random number generators.

Using Non-Persisted Tokens

If your web page or web application is very busy and server storage is limited, you probably want to avoid persisting tokens on the server side. In these cases, you can generate and process tokens cryptographically. With this approach, there is no need to store the token in the server session:

  1. Use symmetric encryption with a key that is only known to the server and never shared.
  2. Combine the current timestamp, the user name, and the form name (if needed), and then encrypt that combination using the server key.
  3. When you receive a token from the web browser, decrypt it using the same key.
  4. Compare the timestamp from the decrypted content with the current timestamp (to eliminate old tokens), compare the decrypted user name with the current user name and compare the decrypted form name with the current form name.

Note that while this method will help you avoid storing a lot of information, it may have a performance overhead because cryptographic functions are more resource-consuming than simple random number generation.

Another option for non-persisted tokens are double-submit cookies. In this technique, the server sets a random value in a cookie for the user even before they authenticate. Then, the server expects that this value is sent with every request (for example, using a hidden form value).

CSRF Protection for Ajax

Anti-CSRF tokens should also be used for Ajax requests. However, before you implement any type of CSRF protection for Ajax, make sure that your web server does not allow cross-domain Ajax requests (check for Cross-Origin Resource Sharing headers).

In the case of Ajax, you can include your token in a hidden text field or directly in JavaScript. You then send the token with every Ajax request and verify its presence server-side.

Anti-CSRF Tokens for Login

It is a common belief that anti-CSRF tokens are needed only when the user is logged in. Therefore, login forms usually do not employ any CSRF protection. While it is not possible to impersonate the user before they are logged in, lack of CSRF protection for login forms may lead to tricking the user into logging in as the attacker and exposing sensitive information. For example, the attack could be done as follows:

  1. The attacker creates an account on your website.
  2. The attacker tricks the victim into logging in on your website using their credentials (this is possible if there is no CSRF protection for the login form).
  3. The victim uses your website and they may not realize they are logged in as someone else.
  4. The attacker may stalk the victim using history functionality or in some other ways profit from the victim using your site.

For these reasons, it is recommended that you include anti-CSRF tokens on all login pages as well.

CSRF Prevention Beyond Tokens

For an in-depth defense against CSRF, you can combine CSRF tokens with other approaches. For example, you can use custom headers to verify Ajax requests. This technique works because under same-origin policy, only JavaScript from the same origin can be used to add request headers. For a detailed discussion of this and other CSRF prevention methods, see the OWASP Cross-Site Request Forgery Prevention Cheat Sheet.

Anti-CSRF tokens are one of the safest ways to defend against CSRF attacks, but they can be bypassed in some circumstances. For example, if the web application has a cross-site scripting vulnerability (XSS), the attacker may exploit it to execute a script that silently fetches a new version of the form with the current CSRF token. To prevent this and maintain solid web application security, make sure you check your web application for all types of vulnerabilities, not just CSRF.