How to Protect PHP Web Forms From CSRF Attacks

Posted on Updated on

CSRF (Cross-Site Request Forgery) attacks are a particularly dangerous form of hacking which can be used to impersonate a customer at any authenticated site. For example, an attacker might be able to access your bank account, your billing sites or your forum posts. To understand CSRF attacks, let’s examine the mechanism.

The Use of Persistent Cookies

When you type your username and password into a site, you often see a little checkbox under the fields saying “Remember Me”, or “Remember this Device”. Enabling that checkbox means that you don’t have to enter your credentials each time you open the website. This is very convenient. Imagine signing into your e-mail each time you want to check it!

How does this work? When you authenticate for the first time, the website places a little file on your computer called a “cookie”. This cookie is unique to you. When you visit the site again, the website checks to see if the cookie is present on your device. If so, it means it’s the same one that was used the last time, and it logs you in. That’s why whenever you clear the cache and cookies on your browser, you’re automatically logged off to all sites – even those for which you had enabled the “Remember Me” checkbox.

How Forms Submit Data via POST Requests

When a website presents you with a form, you fill in the fields and click the “Submit” button. The data you entered gets sent to the server in what is called a POST request. A common example is transferring money to another via your bank. You need to fill in the following fields:

  1. Name of the recipient;
  2. Amount.

This information can even be encoded plainly in the URL like this:

amount=600&recipientID=352

So the server receives this request. But how does it know that it’s coming from you? Simple – the cookie! The server verifies that it’s really you by checking to see if you have a valid cookie, receives the POST request information and executes it. Everything’s as it is supposed to be.

Or is it?

How do CSRF Attacks Work

If you use the facility to “Remember Me”, then it’s almost as if you’re permanently logged into the site. So if an attacker can somehow make your browser send a POST request, it’ll seem as if it’s coming directly from you! For example, you visit a malicious site that uses Javascript to submit a POST request with money transfer information to your bank. When your bank receives the request, it checks your computer for a cookie. If it finds one, it assumes that you have initiated the transfer and executes it.

That’s how CSRF attacks work. This is why any bank worth its salt will log you out after a few minutes of inactivity and won’t allow you to “remember” it. If you close the tab, your session is lost. But other than that, what can you do as a web developer to try and prevent CSRF attacks to keep your customers safe?

CSRF Prevention Strategies

Some of these are only partial solutions. Others are hard counters to CSRFs.

Checking Referrer Information:

The simplest example I gave above was a user visiting a malicious (or hacked) site which submits the POST request via Javascript. Even though the POST information might be valid, the banking site can see the “Referrer” header and figure out that it’s not coming from their own domain. Javascript does not allow you to manually set a different Referrer header – only the browser itself can do that. So checking the referrer information is one way to fend off potential CSRF threats.

Using Randomized Tokens:

A great solution to countering CSRF attacks is if a website or bank generates a unique random token for each session whenever the user logs in. Now whenever the user fills out a form on the banking website, there is also a hidden field which contains the randomized token. The user can’t see it, and there’s no way for anyone else to know what it is.

The POST information will now also contain the random token from the hidden field. In order to authenticate, the server checks to see if the value of the hidden field matches the randomized token it generated at the beginning of the session.

Verification Question Before Submission

A much simpler way to thwart CSRF attacks is to make the user type in a verification question before the POST request is actually carried out. This way even IF the request is successfully spoofed by a third party, the user still has to give their input via an answer and they will be alerted to the potential threat.

Or sites could simply include a CAPTCHA which ensures that a human is sitting in front of the computer and authorizing the request.

In short, there are many good ways to defeat CSRF attacks. They might be deadly if carried out, but web developers have a number of options to make the forms bulletproof and keep their customers safe.

Protecting a PHP Form From CSRF Attacks

Let’s take this basic PHP form and see how we can protect it from CSRF attacks by using ranzomized tokens:

index.php:

<form action="submit.php" method="post">
<input type="text" id="fullname" name="fullname" />
<input type="email" id="email" name="email" />
<input type="submit" name="submit" id="submit" value="Submit" />
</form>

In order to protect the form, we’ll use a PHP class available on GitHub. Download csrf.class.php and save it in the same folder as index.php.

Next you’ll need to modify index.php to include csrf.class.php in order to generate a random token:

index.php:

<?php
session_start();
require_once("csrf.class.php");

$csrf = new csrf();
$token_id = $csrf->get_token_id();
$token_value = $csrf->get_token($token_id);
?>
<form action="submit.php" method="post">
<input type="text" id="fullname" name="fullname" />
<input type="email" id="email" name="email" />
<input type="submit" name="submit" id="submit" value="Submit" />
</form>

Now you must add a new hidden field to hold the token ID and its value:

index.php:

<?php
session_start();
require_once("csrf.class.php");

$csrf = new csrf();
$token_id = $csrf->get_token_id();
$token_value = $csrf->get_token($token_id);
?>
<form action="submit.php" method="post">
<input type="text" id="fullname" name="fullname" />
<input type="email" id="email" name="email" />
<input type="hidden" name="<?= $token_id; ?>" value="<?= $token_value; ?>" />
<input type="submit" name="submit" id="submit" value="Submit" />
</form>

Finally you must check the randomized token data against the values sent through the form:

submit.php:

<?php
session_start();
include 'csrf.class.php';
 
$csrf = new csrf();
$token_id = $csrf->get_token_id();
$token_value = $csrf->get_token($token_id);

if($csrf->check_valid('post')):
   var_dump($_POST[$token_id]);
else:
 echo 'Not Valid';
endif;
?>

If you wish to implement this type of protection to forms on a WordPress site, make sure to disable any types of caching for the page containing the form to avoid the token data from being cached.

Leave a Reply

Your email address will not be published. Required fields are marked *