Csrf
Csrf is a real pipeline middleware — register it with
->middleware() on any state-changing route. It validates
POST/PUT/PATCH/DELETE requests only; safe methods
and configured exclusions pass through untouched.
Registering it
Not final — extend it with a zero-argument constructor that supplies your app's
secret, the same way this site's own skeleton does:
// app/Middlewares/Csrf.php
final class Csrf extends \Monad\Clarity\Middlewares\Csrf
{
public function __construct()
{
parent::__construct(
hmacSecret: (string) getenv('APP_SECRET'),
excludedPaths: [], // e.g. ['/webhooks'] for stateless routes that skip validation
);
}
}
Route::post('/comments', [CommentController::class, 'store'])->middleware(Csrf::class);
Embedding the token
$csrf = new App\Middlewares\Csrf();
$token = $csrf->tokenFor($request); // reuses the existing token, or issues one
<form method="post" action="/comments">
<input type="hidden" name="_csrf" value="<?= htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">
...
</form>
Submitted either as the _csrf form field or an X-CSRF-Token header — useful
for an AJAX/fetch call that can't add a hidden input.
Two storage strategies
-
Session-backed (a valid
midsession cookie is present): the token lives in the session's payload, server-side, keyed by a session id an attacker can't read. This alone defeats forgery. -
Session-less (no session — e.g. a public/anonymous form):
{random}.{timestamp}.{hmac}. The HMAC proves the token was minted by this app and hasn't exceeded its TTL, but it is not bound to a specific browser — the real defense for this path is the Origin/Referer check, where at least one header is required to be present.
Rotating after login
$csrf->rotate($request); // force a fresh token, invalidating the old one — defeats token fixation
Methods
$csrf->tokenFor(Request $request): string
$csrf->rotate(Request $request): string
$csrf->__invoke(Request $request, callable $next): Response // the pipeline entry point
Csrf::FIELD_NAME (_csrf), Csrf::HEADER_NAME (X-CSRF-Token).
Next steps
- Middleware Pipeline — the closure-or-class contract every middleware follows.
- Session — where the session-backed token actually lives.