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 mid session 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.