What is CSRF?
Cross-Site Request Forgery (CSRF) is a web security vulnerability that allows an attacker to trick authenticated users into executing unwanted actions on a website where they're currently logged in. The attack works by exploiting the trust that a website has in a user's browser.
How CSRF Attacks Work
- User logs into legitimate website A and receives a session cookie
- User visits malicious website B while still logged into A
- Website B contains code that makes a request to website A
- The browser automatically includes the session cookie
- Website A processes the request thinking it's legitimate
CSRF Protection Methods in PHP
1. Token-Based Protection Using Hidden Input
This is the most common method. Here's how to implement it:
// In your session initialization (e.g., at login)
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// In your form
function generateFormWithCSRFToken() {
return '<form method="POST" action="/submit">
<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">
<!-- rest of your form fields -->
<input type="submit" value="Submit">
</form>';
}
// In your form processing
function validateCSRFToken() {
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) ||
!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die('CSRF token validation failed');
}
return true;
}
2. CSRF Protection Using Custom Headers
This method uses AJAX requests with custom headers:
// PHP Backend
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// Validate the token
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$headers = getallheaders();
if (!isset($headers['X-CSRF-Token']) ||
!hash_equals($_SESSION['csrf_token'], $headers['X-CSRF-Token'])) {
http_response_code(403);
die('CSRF token validation failed');
}
}
// JavaScript Frontend
const csrfToken = '<?php echo $_SESSION["csrf_token"]; ?>';
fetch('/api/endpoint', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
3. Double Submit Cookie Pattern
This method involves sending the token both as a cookie and as a request parameter:
// Set both cookie and session token
session_start();
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
setcookie('csrf_token', $token, [
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);
// Validation function
function validateDoubleSubmitToken() {
if (!isset($_COOKIE['csrf_token']) ||
!isset($_POST['csrf_token']) ||
!isset($_SESSION['csrf_token'])) {
return false;
}
return hash_equals($_COOKIE['csrf_token'], $_POST['csrf_token']) &&
hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']);
}
4. SameSite Cookie Attribute
Modern applications can also use the SameSite cookie attribute as an additional layer of protection:
// Set cookie with SameSite attribute
session_start();
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => $_SERVER['HTTP_HOST'],
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]);
Best Practices for CSRF Protection
-
Token Generation
- Use cryptographically secure random number generators
- Make tokens sufficiently long (at least 32 bytes)
- Generate new tokens for each session
function generateSecureToken($length = 32) {
return bin2hex(random_bytes($length));
}
-
Token Validation
- Use timing-safe comparison functions
- Validate token presence and value
- Implement proper error handling
function validateToken($userToken, $storedToken) {
if (empty($userToken) || empty($storedToken)) {
return false;
}
return hash_equals($storedToken, $userToken);
}
-
Form Implementation
- Include tokens in all forms
- Implement automatic token injection
- Handle token rotation
class CSRFProtection {
public static function getTokenField() {
return sprintf(
'<input type="hidden" name="csrf_token" value="%s">',
htmlspecialchars($_SESSION['csrf_token'])
);
}
}
Framework-Specific Protection
Many PHP frameworks provide built-in CSRF protection:
Laravel Example
// In your form
@csrf
// Manual token generation
{{ csrf_field() }}
Symfony Example
// In your form
{{ csrf_token('form_name') }}
Common Pitfalls to Avoid
- Don't use predictable tokens
- Don't store tokens in JavaScript variables accessible globally
- Don't skip CSRF protection for AJAX requests
- Don't rely solely on checking the Referer header
- Don't use the same token for multiple forms
Remember that CSRF protection should be part of a broader security strategy that includes proper session management, secure cookie handling, and input validation.
Top comments (0)