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 of hash_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.