DEV3 Technologies home

DEV3 Developer Forum

How do I add CSRF protection to plain PHP forms?

PHP helpSolved0 views
dev3 ·

I have a plain PHP site without a framework. Forms post to update profile data and delete items. How do I implement CSRF tokens correctly with sessions?

#php#security#csrf

0

1 reply

dev3 · Accepted answer

A CSRF attack makes a logged in user's browser send a state-changing request to your site from another site. The defence is a secret, per-session token that the attacker cannot read.

Generate and store the token

session_start();

function csrf_token(): string
{
    if (empty($_SESSION['csrf'])) {
        $_SESSION['csrf'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf'];
}

function csrf_check(?string $token): void
{
    if (!is_string($token) || !hash_equals($_SESSION['csrf'] ?? '', $token)) {
        http_response_code(419);
        exit('Invalid CSRF token');
    }
}

Put it in forms

<form method="post" action="/profile.php">
  <input type="hidden" name="csrf" value="<?= htmlspecialchars(csrf_token(), ENT_QUOTES) ?>">
  ...
</form>

Verify on every POST, PUT, DELETE

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    csrf_check($_POST['csrf'] ?? null);
    // proceed
}

For fetch/AJAX send the token in a header (X-CSRF-Token) and read it with $_SERVER['HTTP_X_CSRF_TOKEN'].

Additional layers

  • Set the session cookie with SameSite=Lax (or Strict), Secure and HttpOnly. SameSite alone blocks most cross-site POSTs, but keep tokens for defence in depth.
  • Never change state with GET requests. A delete link such as /delete.php?id=5 can be triggered by an image tag. Use POST plus a token.
  • Use hash_equals() to compare, to avoid timing leaks.
  • Regenerate the session id at login (session_regenerate_id(true)).
  • Tokens must not be in the URL (logs and Referer leak).

Pitfalls

  • A token stored per session and reused is acceptable for most sites; per-form tokens are optional.
  • If your page is cached by a CDN, the token would be cached with it. Do not cache pages containing forms.
  • Check the Origin or Referer header too if you want extra protection on sensitive actions.
0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.