Saltar al contenido principal

Introducción

CSRF (Cross-Site Request Forgery) es un ataque que hace enviar solicitudes no deseadas suplantando a un usuario que ya ha iniciado sesión. Por ejemplo, imagina que tu aplicación tiene una ruta POST /user/email que acepta el cambio de dirección de correo. Si un atacante coloca en otro sitio un formulario que envía automáticamente a esa URL, es posible que se cambie la dirección de correo sin que el usuario se dé cuenta. En Laravel 13, la protección CSRF está habilitada por defecto gracias al mecanismo incluido en el grupo de middleware web.

Prevención de ataques CSRF

El middleware PreventRequestForgery de Laravel evita los CSRF con dos capas:
  1. Validación de Origin (cabecera Sec-Fetch-Site)
  2. Validación de token (token CSRF por sesión)
Primero comprueba el Origin; si no puede determinarse o falla, recurre a la validación de token.

Validación de Origin

Laravel comprueba primero Sec-Fetch-Site para decidir si la solicitud proviene del mismo origen. Esto es especialmente útil en entornos HTTPS. Si la validación de Origin tiene éxito, la solicitud se permite en ese punto. Si no la supera, se ejecuta la validación de token CSRF como hasta ahora.
Sec-Fetch-Site se presupone en conexiones HTTPS. En entornos HTTP la validación de Origin no funciona y la validación de token es la principal defensa.

Modo Origin-only

También puedes desactivar el fallback a la validación de token y decidir únicamente por la validación de Origin.
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->preventRequestForgery(originOnly: true);
    });
En el modo Origin-only, las solicitudes que no superen la validación de Origin devuelven 403 en lugar de 419. Si necesitas permitir solicitudes same-site, por ejemplo entre subdominios, configura allowSameSite.
$middleware->preventRequestForgery(allowSameSite: true);

Validación de token

Laravel genera un token CSRF por cada sesión. Puedes obtenerlo con csrf_token() o desde la sesión.
use Illuminate\Http\Request;

Route::get('/token', function (Request $request) {
    $tokenFromSession = $request->session()->token();
    $tokenFromHelper = csrf_token();
});
En las rutas web, cuando crees formularios POST, PUT, PATCH o DELETE, incluye siempre @csrf.
<form method="POST" action="/profile">
    @csrf

    <!-- Input hidden equivalente -->
    <input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>

Exclusión de URI

En solicitudes provenientes de servicios externos, como los webhooks de Stripe, puede que necesites excluir ciertas URI de la protección CSRF.
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->preventRequestForgery(except: [
            'stripe/*',
            'http://example.com/foo/bar',
            'http://example.com/foo/*',
        ]);
    });
Siempre que sea posible, coloca las rutas de webhook fuera del grupo de middleware web y reduce al mínimo las exclusiones.

Cabecera X-CSRF-TOKEN

Laravel no solo valida el _token del formulario, también la cabecera X-CSRF-TOKEN. Primero, expón el token en una meta etiqueta.
<meta name="csrf-token" content="{{ csrf_token() }}">
Después, incluye ese valor en la cabecera de las solicitudes AJAX.
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': document
            .querySelector('meta[name="csrf-token"]')
            .getAttribute('content'),
    },
});

Cabecera X-XSRF-TOKEN

Laravel también envía una cookie XSRF-TOKEN cifrada. Axios y Angular pueden asignar automáticamente ese valor a la cabecera X-XSRF-TOKEN en solicitudes al mismo origen. Por eso, en implementaciones SPA o AJAX puede que la protección CSRF quede activa sin necesidad de escribir manualmente la cabecera.

Consideraciones para SPA

Al usar Laravel como backend de API para una SPA, primero obtén la cookie CSRF y después envía la solicitud de inicio de sesión.
await axios.get('/sanctum/csrf-cookie');
await axios.post('/login', {
    email: '[email protected]',
    password: 'password',
});
Consulta Sanctum para más detalles.
Última modificación el 13 de julio de 2026