Cross Site Request Forgery

What Is CSRF?


Cross-Site Request Forgery (also known as CSRF or XSRF attacks) is an attack which allows attackers to execute undesired actions on a web application in which a user currently is authenticated. The attack is possible when the targeted application does not properly validate the origin of the request, and relies only on the existence of a valid session between the victim's browser and the application server.

In the most common scenario of a CSRF attack, a logged-on user will access an additional web page provided by the attacker in another tab of the browser. This page will immediately target a sensitive function within the application – which is still open in another tab – by submitting a specially crafted request. Since the request is submitted from the same browser, the vulnerable application will accept the request and execute the action.

csrf.ping_.png

Protection


Proper CSRF protection is based on preventing attackers from being able to create a granular request for actions in a system. A solution to this type of attack is to implement unique random tokens for sensitive forms. For each form submission, the token should be validated on the server side.

As a side note, these tokens should always be submitted using the POST method. They are usually supplied as a hidden form field.

PHP Implementation Example


Generating a secure CSRF token:
function generateCSRFKey($key) {
    $token = base64_encode(openssl_random_pseudo_bytes(16));
    $_SESSION['csrf_' . $key] = $token;
    return $token;
}

You may be tempted to use rand() or uniqid() but they both specifically state that these functions should not be used for generating secure tokens! Also base64_encode() is only used to make sure the value doesn't break any HTML code.

Checking a submitted token is valid:
function checkCSRFKey($key, $value) {
    if (!isset($_SESSION['csrf_' . $key]))
        return false;
    if (!$value)
        return false;

    if ($_SESSION['csrf_' . $key] !== $value)
        return false;

    unset($_SESSION['csrf_' . $key]); 
    return true;
}

The above code can be used to add a unique token to any form using:
<input type="hidden" value="<?=generateCSRFKey('settings');?>" name="token">

Then the first check in the server-side code that handles the form should be that the supplied token is valid:
$token = $_POST['token'];
if (checkCSRFKey('settings', $token)) {
    // Handle error
}

Comments