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)