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.
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
}