×

php · level 3 · unit 5 · lesson 4

  1. meet it
  2. guess first
  3. your turn
  4. fix it
  5. nailed it

Tap a green word if you get lost.

meet it

A proves the form really came from your site.

You are logged in to your bank. In another tab, a nasty page secretly sends a form to the bank's transfer address. Your browser adds your login cookie, so the bank obeys. This is CSRF, cross-site request forgery.

The fix: when you show a form, make a token with bin2hex(random_bytes(32)). That is 32 unguessable bytes written as 64 letters and digits. Store it in the session and hide it in the form. A real submission sends it back, but a forged one cannot know it.

Compare tokens with hash_equals, not ===, which stops at the first wrong character and leaks timing clues. That stops timing attacks.

<?php
$_SESSION["csrf"] = bin2hex(random_bytes(32));
echo "Token made: " . strlen($_SESSION["csrf"]) . " characters\n";
$_SERVER["REQUEST_METHOD"] = "POST";
$_POST = ["amount" => "500", "csrf" => "guessed-token"];
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    if (hash_equals($_SESSION["csrf"], $_POST["csrf"] ?? "")) {
        echo "Transfer of £" . $_POST["amount"] . " accepted.\n";
    } else {
        echo "Refused: this form did not come from us.\n";
    }
}

This code pretends a forged POST request by filling $_SERVER, $_POST and $_SESSION at the top.