symfony/security-csrf
Symfony Security CSRF component provides CsrfTokenManager to generate, store, and validate CSRF tokens, protecting forms and requests against cross-site request forgery. Integrates cleanly with Symfony apps and can be used standalone in PHP projects.
Install the Package:
composer require symfony/security-csrf
Ensure compatibility with Laravel’s Symfony components (e.g., symfony/http-foundation).
Register the Token Manager:
Bind CsrfTokenManagerInterface in config/app.php or a service provider:
$app->bind(CsrfTokenManagerInterface::class, function ($app) {
return new SameOriginCsrfTokenManager(
new SessionCsrfTokenStorage($app['session']),
'meta' // Token name (e.g., '_token' for Laravel compatibility)
);
});
Generate a Token in a Blade View:
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
$token = app(CsrfTokenManagerInterface::class)->getToken('meta')->getValue();
Or inject the manager into a controller:
public function showForm(CsrfTokenManagerInterface $csrf) {
return view('form', ['csrf_token' => $csrf->getToken('meta')->getValue()]);
}
Validate in a Controller:
public function submitForm(Request $request, CsrfTokenManagerInterface $csrf) {
$token = $request->input('_token'); // Laravel's default field name
if (!$csrf->isTokenValid(new CsrfToken('meta', $token))) {
throw new \Symfony\Component\Security\Core\Exception\AccessDeniedException('Invalid CSRF token.');
}
// Process request...
}
Leverage Laravel’s Middleware (Optional):
Extend Laravel’s VerifyCsrfToken to use Symfony’s validator:
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
public function handle($request, Closure $next) {
$csrf = app(CsrfTokenManagerInterface::class);
if (!$csrf->isTokenValid(new CsrfToken('meta', $request->input('_token')))) {
abort(419, 'CSRF token mismatch.');
}
return $next($request);
}
// In your controller or Blade view
$csrfToken = app(CsrfTokenManagerInterface::class)->getToken('meta')->getValue();
<form method="POST" action="/submit">
<input type="hidden" name="_token" value="{{ $csrfToken }}">
<!-- Form fields -->
</form>
public function submit(Request $request) {
$csrf = app(CsrfTokenManagerInterface::class);
if (!$csrf->isTokenValid(new CsrfToken('meta', $request->input('_token')))) {
return back()->withErrors(['csrf' => 'Invalid token.']);
}
// Proceed...
}
SessionCsrfTokenStorage (default), CookieCsrfTokenStorage, or custom implementations.'meta' or align with Laravel’s _token convention.HeaderCsrfTokenStorage or CookieCsrfTokenStorage for header/cookie-based tokens.// Controller
public function create(CsrfTokenManagerInterface $csrf) {
$token = $csrf->getToken('meta')->getValue();
return view('form', compact('token'));
}
<input type="hidden" name="_token" value="{{ app('csrf')->getToken('meta')->getValue() }}">
return response()->json(['csrf_token' => $csrf->getToken('meta')->getValue()]);
public function handle($request, Closure $next) {
$csrf = app(CsrfTokenManagerInterface::class);
$token = $request->input('_token') ?? $request->header('X-CSRF-Token');
if (!$csrf->isTokenValid(new CsrfToken('meta', $token))) {
abort(419);
}
return $next($request);
}
public function update(Request $request) {
$this->validateCsrf($request);
// Business logic...
}
protected function validateCsrf(Request $request) {
$csrf = app(CsrfTokenManagerInterface::class);
if (!$csrf->isTokenValid(new CsrfToken('meta', $request->input('_token')))) {
throw new \RuntimeException('CSRF validation failed.');
}
}
// Generate token
$token = $csrf->getToken('api_token')->getValue();
// Embed in response headers
return response()->json([], 200, [
'X-CSRF-Token' => $token,
]);
$token = $request->header('X-CSRF-Token');
if (!$csrf->isTokenValid(new CsrfToken('api_token', $token))) {
abort(403);
}
use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface;
class DatabaseCsrfTokenStorage implements TokenStorageInterface {
public function getToken(string $tokenId): ?CsrfToken {
return CsrfToken::fromString($tokenId, DB::table('csrf_tokens')->where('id', $tokenId)->value('token'));
}
// Implement other methods...
}
class RedisCsrfTokenStorage implements TokenStorageInterface {
public function __construct(private Redis $redis) {}
public function getToken(string $tokenId): ?CsrfToken {
return CsrfToken::fromString($tokenId, $this->redis->get($tokenId));
}
// ...
}
Service Provider Binding:
public function register() {
$this->app->singleton(CsrfTokenManagerInterface::class, function ($app) {
return new SameOriginCsrfTokenManager(
new SessionCsrfTokenStorage($app['session']),
'_token' // Laravel's default field name
);
});
}
Middleware Integration:
Replace or extend Laravel’s VerifyCsrfToken:
public function handle($request, Closure $next) {
if ($this->isReading($request) || $this->shouldPassThrough($request)) {
return $next($request);
}
$csrf = app(CsrfTokenManagerInterface::class);
if (!$csrf->isTokenValid(new CsrfToken('meta', $request->input('_token')))) {
abort(419);
}
return $next($request);
}
API Resource Protection:
Use Symfony’s CsrfTokenManager in Laravel Sanctum or Passport:
public function store(Request $request) {
$this->validateCsrf($request); // Custom validation
// Sanctum/Passport logic...
}
Token Rotation: Regenerate tokens after submission to mitigate replay attacks:
public function submit(Request $request) {
$csrf = app(CsrfTokenManagerInterface::class);
$csrf->getToken('meta')->regenerate(); // Rotate token
// Process request...
}
Double Submit Cookie Pattern:
Combine Symfony’s CSRF with Laravel’s XSRF-TOKEN cookie:
// Generate token and set cookie
$token = $csrf->getToken('meta')->getValue();
$response = new Response();
$response->headers->setCookie(new Cookie('XSRF-TOKEN', $token));
Custom Token Generators:
Extend CsrfTokenManager for domain-specific tokens:
class CustomCsrfTokenManager extends SameOriginCsrfTokenManager {
public function generateToken(string $tokenId): CsrfToken {
$token = parent::generateToken($tokenId);
return new CsrfToken($tokenId, $token->getValue() . '-' . Str::random(4));
}
}
How can I help you explore Laravel packages today?