php · level 3 · unit 5 · lesson 4
- meet it
- guess first
- your turn
- fix it
- 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.
guess first
A real form sends its token back. What will this show?
Guess before you run it. Wrong guesses are how you learn, and nothing is marked.
<?php
$_SESSION["csrf"] = bin2hex(random_bytes(16));
$_POST["csrf"] = $_SESSION["csrf"];
if (hash_equals($_SESSION["csrf"], $_POST["csrf"])) {
echo "Accepted: " . strlen($_POST["csrf"]) . " character token\n";
} else {
echo "Refused\n";
}
your turn
Your turn. Protect the comment form.
Type how many random bytes the token should use, 32 is a good choice, then type the safe function for comparing secrets.
fix it
Sam's real request is refused. Fix it.
- Line 2 pretends the token stored when the form was shown, and line 3 is the form coming back with it. Which line changes the token in between?
- A fresh token is made before the check, so the one the form sent no longer matches anything.
- Move line 4 to the very end, after the
ifandelse, then run it.
nailed it
your forms only trust themselves now. :D
- A CSRF token proves a form came from your own site.
- Make tokens with
random_bytesandbin2hex, never guessable values. - Compare secrets with
hash_equalsto stop timing attacks.
new words in your collection
- CSRF
- token
- timing attack
This lesson isn’t saved. A free account remembers every lesson you finish.