security
Cross-Site Request Forgery (CSRF)
CSRF tricks a user's browser into sending an unwanted state-changing request while the browser automatically includes authentication cookies. A CSRF token proves the request came through a form or client flow created by the application.
What Matters
- Protect cookie-authenticated state changes such as updates, deletes, purchases, and password changes.
- Generate tokens with cryptographically secure randomness and bind them to the user session.
- Compare tokens with
hash_equals(). - Use
SameSitecookies as an additional layer, not the only defence. - Do not use GET requests for state-changing actions.
Practical Example
<?php
declare(strict_types=1);
function validCsrfToken(string $sessionToken, string $submittedToken): bool
{
return $sessionToken !== '' && hash_equals($sessionToken, $submittedToken);
}
echo validCsrfToken('known-token', 'known-token') ? 'accepted' : 'rejected';
echo PHP_EOL;
echo validCsrfToken('known-token', 'wrong-token') ? 'accepted' : 'rejected';
echo PHP_EOL;
// Prints:
// accepted
// rejected
In Application Work
Frameworks usually provide CSRF middleware and form helpers. In maintenance work, check that custom endpoints, JSON requests, and legacy forms have not bypassed that protection.
Why Authentication Is Not Enough
A browser automatically sends matching cookies with requests. If a user is logged in to a shop, another website may be able to submit a form to that shop from the user's browser. The request carries the real session cookie even though the user did not intend the action.
The attacker usually cannot read the application's pages because of browser origin rules. A CSRF token works because the attacker also needs an unpredictable value from the legitimate application flow.
Token Placement
Traditional forms send the token in a hidden input. JavaScript clients often send it in a request header. The server compares the submitted token with the value bound to the current session.
<input type="hidden" name="_csrf" value="...">
Regenerate or rotate tokens according to the framework's established policy. Do not invent a weaker custom token format when the framework already provides tested middleware.
When CSRF Applies
CSRF primarily matters when browsers automatically attach credentials, especially cookies. A bearer token that a client explicitly adds to an Authorization header changes the analysis, but XSS and token storage risks still remain.
Use safe HTTP methods for reads. A GET request should not delete data, trigger a payment, or change account settings.
What To Check
Before moving on, make sure you can:
- explain why browser cookies make CSRF possible;
- protect every cookie-authenticated state change;
- generate and compare tokens safely;
- use SameSite cookies as defence in depth;
- identify which browser-authenticated routes need CSRF review.
Practice
Practice: Verify A CSRF Token
Build a token generator and verifier for a session-backed form.
Requirements
- Generate a token from secure random bytes.
- Reject missing tokens.
- Compare supplied tokens with
hash_equals(). - Show accepted and rejected verification results.
Show solution
The token is unpredictable and comparison is explicit.
<?php
declare(strict_types=1);
function generateCsrfToken(): string
{
return bin2hex(random_bytes(32));
}
function verifyCsrfToken(?string $known, ?string $submitted): bool
{
return is_string($known)
&& $known !== ''
&& is_string($submitted)
&& hash_equals($known, $submitted);
}
$token = generateCsrfToken();
echo verifyCsrfToken($token, $token) ? 'accepted' : 'rejected';
echo PHP_EOL;
echo verifyCsrfToken($token, null) ? 'accepted' : 'rejected';
echo PHP_EOL;
// Prints:
// accepted
// rejected
A real application stores the generated token in the user session and sends it in the form or request header.