PHP 8: Modern PHP Development · Security for PHP Applications
CSRF Protection and Secure Forms
Protect state-changing requests with tokens, SameSite cookies and method discipline.
Cross-site request forgery tricks a logged-in user's browser into submitting a request to your site. Defend with synchronizer tokens (random per-session token in a hidden field, validated with hash_equals()), SameSite cookies, and by never changing state on GET. Frameworks (Symfony, Laravel) include CSRF middleware — keep it enabled for cookie-authenticated routes.
<?php
function csrfToken(): string
{
return $_SESSION['csrf'] ??= bin2hex(random_bytes(32));
}
function verifyCsrf(): void
{
$sent = $_POST['_csrf'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
if (!is_string($sent) || !hash_equals($_SESSION['csrf'] ?? '', $sent)) {
http_response_code(419);
exit('Invalid CSRF token');
}
}
?>
<form method="post" action="/account/email">
<input type="hidden" name="_csrf" value="<?= e(csrfToken()) ?>">
<input type="email" name="email" required>
<button>Update</button>
</form>
Common pitfalls
- Changing state via GET links (e.g.,
/delete?id=5). - Comparing tokens with
==instead ofhash_equals(). - Disabling CSRF middleware for convenience on session-authenticated APIs.
Key points
- Random tokens validated with hash_equals
- SameSite=Lax cookies
- No state changes on GET
Further reading: OWASP CSRF Prevention Cheat Sheet
Practice questions
Do token-authenticated JSON APIs need CSRF tokens?
Answer
If the token is sent in an Authorization header (not automatically by the browser), CSRF isn't possible because a cross-site page can't attach it. Cookie-based auth needs CSRF protection.
Why use hash_equals() for token comparison?
Answer
It compares in constant time, preventing timing attacks that could reveal the token byte by byte.
Open in the app to mark lessons complete, take notes and track your progress.