PHP CSRF Protection
In this page:
session_start();
$_SESSION["csrf_token"] = bin2hex(random_bytes(32));
// in the form:
// <input type="hidden" name="csrf_token" value="<?= $_SESSION["csrf_token"] ?>">
if (hash_equals($_SESSION["csrf_token"], $_POST["csrf_token"])) {
// request is genuine
}
What is CSRF?
Cross-Site Request Forgery tricks a logged-in user's browser into submitting a request to your site without their knowledge, by embedding a malicious form or link on another page they happen to visit.
Example: What is CSRF?
<?php
// A malicious page could auto-submit this hidden form using the victim's session
echo '<form action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="amount" value="1000">
</form>';
?>
Login to try C/C++/Java/PHP code in the editor
Generating a CSRF Token
A CSRF token is a random, unpredictable value generated per session (or per form) and embedded as a hidden form field, which your server then verifies matches on submission — an attacker's forged request has no way to know this value.
Example: Generating a CSRF Token
<?php
// Call `session_start()`
session_start();
// Set `$_SESSION['csrf_token']` to `bin2hex(random_bytes(16))`
$_SESSION['csrf_token'] = bin2hex(random_bytes(16));
// Print `$_SESSION['csrf_token']` to the output
echo $_SESSION['csrf_token'];
?>
Login to try C/C++/Java/PHP code in the editor
Adding Tokens to Forms
Generate the token with a cryptographically secure function like random_bytes(), store it in $_SESSION, and compare it using hash_equals() on submission to avoid timing-attack vulnerabilities in the comparison itself.
Example: Adding Tokens to Forms
<?php
// Call `session_start()`
session_start();
// Set `$_SESSION['csrf_token']` to `bin2hex(random_bytes(16))`
$_SESSION['csrf_token'] = bin2hex(random_bytes(16));
// Print '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">' to the output
echo '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';
?>
Login to try C/C++/Java/PHP code in the editor
Verifying the CSRF Token
CSRF protection matters specifically for state-changing requests (anything that modifies data, like a password change or a purchase) — read-only GET requests are typically not the concern this defense addresses.
Example: Verifying the CSRF Token
<?php
// Call `session_start()`
session_start();
// Set `$_SESSION['csrf_token']` to "abc123"
$_SESSION['csrf_token'] = "abc123";
// Set `$_POST['csrf_token']` to "abc123"
$_POST['csrf_token'] = "abc123";
// Check whether `hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])`
if (hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
// Print "Token valid -- proceed with the state-changing action" to the output
echo "Token valid -- proceed with the state-changing action";
// Otherwise, run this branch
} else {
// Print "Token mismatch -- reject request" to the output
echo "Token mismatch -- reject request";
}
?>
Login to try C/C++/Java/PHP code in the editor
Clearing Used Tokens
Most PHP frameworks like Laravel and Symfony generate and verify CSRF tokens automatically for you, which is one of the strong reasons to prefer a framework's form helpers over hand-rolling raw HTML forms for anything that changes data.
Example: Clearing Used Tokens
<?php
// Call `session_start()`
session_start();
// Set `$_SESSION['csrf_token']` to "abc123"
$_SESSION['csrf_token'] = "abc123";
// Remove `$_SESSION['csrf_token']`
unset($_SESSION['csrf_token']);
// Print `isset($_SESSION['csrf_token']) ? "Still set" : "Token cleared after use"` to the output
echo isset($_SESSION['csrf_token']) ? "Still set" : "Token cleared after use";
?>
Login to try C/C++/Java/PHP code in the editor
- Using a predictable token instead of
random_bytesorbin2hex(random_bytes(32)). - Comparing tokens with
==, whenhash_equalsavoids timing attacks. - Forgetting to check the token on the server for POST requests.
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: