Protecting Your Website Using an Anti-CSRF Token

Category: Web Security Readings - Last Updated: Fri, 19 Jul 2019 - by Netsparker Security Team

It is crucial to make sure that your website or web application security policy includes measures against Cross-Site Request Forgery (CSRF/XSRF) attacks. These attacks may not be dangerous to you directly, but your users may be targeted if you are not protected. This, in turn, could lead to a loss of reputation, loss of sensitive information, or more serious consequences.

Protecting Your Website Using an Anti-CSRF Token

Luckily, in most cases, you can protect your users against CSRF attacks in a simple and very effective way: using anti-CSRF tokens.

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 (token) and check if the web browser sends it back. This piece of information must be 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.

For example, you run a social media web application on www.example.com. 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"/>
</form>

This causes the web browser to send a POST request:

POST /post.php HTTP/1.1
Host: example.com

subject=subject&content=content

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: example.biz."/>
  <input type="submit" value="Submit"/>
</form>
<script>
  document.forms[0].submit();
</script>

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

POST /post.php HTTP/1.1
Host: example.com

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

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.

<form>
  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"/>
</form>

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

POST /post.php HTTP/1.1
Host: example.com

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 tokens in HTTP GET requests so that they are not directly available in the URL and they do not leak in the Referer header.

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 website or web application is very busy and your server storage is limited, you may want to avoid persisting tokens on your end. If so, you may use a cryptographic approach instead. With such an 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 support cross-site 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. 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.

Safety of Tokens

Anti-CSRF tokens are one of the safest ways to defend against Cross-site Request Forgery but they can be bypassed in some circumstances. For example, if the web application also has a Cross-site Scripting vulnerability. If the application is susceptible to XSS, the attacker may execute a script that silently fetches the new version of the form with the current anti-CSRF token. To protect against this situation, make sure to check your web application for all types of vulnerabilities.

Netsparker

Dead accurate, fast & easy-to-use Web Application Security Scanner

GET A DEMO