Cross-Site Request Forgery (CSRF) is an attack that forces authenticated users to execute unwanted actions on a web application in which they're currently authenticated. The attack works by tricking the victim into clicking a link or loading a page that contains a malicious request.
Account Number: 123456789
Date | Description | Amount |
---|---|---|
2025-03-20 | Salary | $3,000.00 |
2025-03-15 | Grocery Store | $150.00 |
2025-03-10 | Gas Station | $45.00 |
This is a malicious website that contains a hidden form targeting the banking application.
Click the button below to claim your prize now!
When the victim clicks the button above, it submits a hidden form to the bank application that transfers $1,000 to the attacker's account. Since the victim is already logged into the bank, the request includes their session cookie, making it appear legitimate.
<form id="csrf-form" action="http://example.com/bank.php" method="post"> <input type="hidden" name="recipient" value="hacker"> <input type="hidden" name="amount" value="1000"> <input type="hidden" name="transfer" value="1"> </form>
Account Number: 123456789
Date | Description | Amount |
---|---|---|
2025-03-20 | Salary | $3,000.00 |
2025-03-15 | Grocery Store | $150.00 |
2025-03-10 | Gas Station | $45.00 |
<img src="https://bank.com/transfer?to=attacker&amount=1000">
The most common defense is to include a unique, unpredictable token with each request that requires a state change:
// Generate a token $csrf_token = bin2hex(random_bytes(32)); $_SESSION['csrf_token'] = $csrf_token; // Include in HTML form echo '<input type="hidden" name="csrf_token" value="' . $csrf_token . '">'; // Verify on submission if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) { // Invalid token, reject the request }
Modern browsers support the SameSite attribute which controls when cookies are sent with cross-site requests:
// Strict: Cookies are only sent for same-site requests setcookie('session', $value, ['samesite' => 'Strict']); // Lax: Cookies are sent for same-site requests and top-level navigations setcookie('session', $value, ['samesite' => 'Lax']); // Default in modern browsers
Require a custom header that browsers won't allow in cross-site requests due to CORS restrictions:
// JavaScript adds a custom header const xhr = new XMLHttpRequest(); xhr.open('POST', '/api/transfer'); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); // Server verifies the header exists if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest') { // Reject the request }
Set a random cookie value and require the same value as a request parameter:
// Set a cookie with a random value setcookie('csrf', $randomValue); // Include the same value in the form echo '<input type="hidden" name="csrf" value="' . $randomValue . '">'; // Verify values match if ($_COOKIE['csrf'] !== $_POST['csrf']) { // Reject the request }
Require the user to confirm their identity before critical actions:
// Before processing a sensitive action if (!isset($_POST['password'])) { // Show confirmation form } else { // Verify password matches the user's password if (password_verify($_POST['password'], $user['password_hash'])) { // Process the action } }