OWASP A5 – Cross-Site Request Forgery (CSRF) with PHP

We are going to cover Cross-Site Request Forgery (CSRF) countermeasures in this post. This is an often overlooked but potentially deadly vulnerability that can be easily remediated. CSRF is an attack that enables a malicious website to execute procedures on a web application for which the victim has already authenticated.

For example, suppose a user logs into their bank account and then accidentally browses to a malicious site. The malicious site has code that submits a money transfer request to the banking application. If the user didn’t correctly logout and end the session, and the banking application doesn’t have an anti-CSRF protection in place, then the transaction will be executed. This happens because the browser sends the request along with the session cookie/data, and if the application has no countermeasures in place it has no way of knowing that the transaction was unintended.

There isn’t really a section dedicated to CSRF in the ASVS guide. There is a single control referenced in section 11 due to the fact that while the impact of a CSRF vulnerability can be great, the control is fairly simple to implement.

ASVS 11.7 Requirement:

Verify that the application generates a strong random token as part of all links and forms associated with transactions or accessing sensitive data, and that the application verifies the presence of this token with the proper value for the current user when processing these requests.

ASVS 11.7 Solution:

The first step in protecting your applications against CSRF attacks is to read and implement all of our recommendations for preventing XSS attacks. An XSS vulnerability can be used to bypass CSRF defenses.

The next step in defending against CSRF attacks is to implement a token that is used to identify valid form submissions. This token should be unique to the session, hard to guess (like a session identifier), and sent via the HTTP POST method to prevent logging of the value or storage of the value in browser history (this also makes CSRF attacks slightly more difficult). Modify your login page such that successful authentication results in the creation of a server side session variable in which to store the token:

  // Set the token by creating a SHA 512 hash of
  // the current session identifier.
  $_SESSION['token'] = hash("sha512", session_id() . 
    openssl_random_pseudo_bytes(32));

 
This creates a token that is tied to the session and combined with a 32bit length random value, then hashed with a strong hash algorithm. Then, in forms submitting sensitive data (really probably best to just implement this in every form just in case), you would add this as a hidden value:

  <form method="POST">
    <input type="text" name="transaction">
    <input type="hidden" name="token" value=" . $_SESSION['token'] . ">
  </form>

 
In your form submission validation code, you would validate that the submitted token matches the one created for the session. As always, perform all necessary input validation and output encoding on the value (not show here, please see the XSS posts for more info):

  // Validate that neither the form token nor the 
  // session token variable are empty and that
  // both tokens match.
  if (!empty($_POST['token']) && !empty($_SESSION['token']) && 
      $_SESSION['token'] == $_POST['token']) {
    // Proceed
    ...
    // Set token to a new value
    $_SESSION['token'] = hash("sha512", session_id() . 
      openssl_random_pseudo_bytes(32));
  } else {
    // Log and present invalid data/submission error
  }

 
Not only does the above code validate the token, but it generates and sets a new token for the next successful form entry. This ensures that even if an attacker obtains a CSRF token during the length of a valid session, it can only be used once. It also reduces the risk of useful token theft as the value changes upon each successful modification. And that is really all there is to it. Simple, yet effective defense against CSRF (as long as your site doesn’t contain XSS vulnerabilities).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: