Preventing CSRF in PHP

Preventing CSRF in PHP is fairly easy if known how to do so, however there are many websites that are vulnerable to this type of attack.

Cross Site Request Forgery (CSRF) is a type of attack that occurs when a malicious site or email causes a user’s browser to perform an unwanted action on a trusted site for which the user is currently authenticated.

For example, let’s assume that a user is logged into a system that manages customer information. URLs on many systems reflect the actions that they perform.

For example, to add a customer, the URL might be: http://www.somesystem.com/customer/add

Similarly, to edit and delete a customer, the URLs might be http://www.somesystem.com/customer/edit/id/1/ and http://www.somesystem.com/customer/delete/id/1 respectively.

The problem with this is that attackers could put a link on a web page or email and try to get the unsuspected user to click on it, therefore causing the user to delete customer information.

Even easier, they could get the user to go to some page of the attacker’s website which does nothing more than redirect to the deletion URL.

To prevent this from happening, add a unique key or token to each form through a hidden input field.

<form ...>
    <input type="hidden" name="form_key" value="<?php echo getFormKey(); ?>" />
    <!-- Some other fields below -->
</form>

The key would come from a simple method function that randomly generates a string and adds it to the session data, like the below for example:

function getFormkey() {
    if (!isset($_SESSION['form_key'])) {
        $formKey = bin2hex(openssl_random_pseudo_bytes(32));
        $_SESSION['form_key'] = $formKey;
    } else {
        $formKey = $_SESSION['form_key'];
    }
    return $formKey;
}

When the form is submitted, you can then compare the form key submitted in the form field with the form key stored in the session.

$formKey = $_REQUEST['form_key'];

if ($formKey == $_SESSION['form_key']) {
    // Okay to proceed
} else {
    // Potential CSRF attack
}

Additional measures can also be taken such as requiring the user to re-login for sensitive operations.

Note: This article is based on PHP version 5.5.