domingo, 15 de junio de 2025

Laravel Sanctum: protección contra CSRF

 ¿Qué protección contra CSRF (Cross-Site Request Forgery) ocurre cuando se usa Sanctum para autenticar una SPA (single-page application)?


En el contexto de la autenticación de SPAs con Laravel Sanctum (cuando el frontend y el backend comparten un "first-party domain"), la protección CSRF (Cross-Site Request Forgery) es un elemento de seguridad crucial que se implementa de una manera ligeramente adaptada a las necesidades de las aplicaciones de una sola página.

Primero, recordemos qué es CSRF:

Un ataque CSRF ocurre cuando un sitio web malicioso engaña al navegador de un usuario ya autenticado para que realice una solicitud no deseada (ej. cambiar la contraseña, hacer una compra) en un sitio legítimo donde el usuario tiene una sesión activa. El navegador envía automáticamente las cookies de sesión con la solicitud, lo que hace que parezca una solicitud legítima del usuario.

¿Cómo funciona la Protección CSRF en Laravel Sanctum (para SPAs)?

Laravel Sanctum extiende el mecanismo de protección CSRF tradicional de Laravel para que funcione sin problemas con las SPAs. Esto se logra mediante un intercambio de tokens que involucra una cookie y un encabezado HTTP:

  1. Paso Inicial: Obtener el XSRF-TOKEN (Solicitud a /sanctum/csrf-cookie)

    • Tu aplicación frontend (SPA) debe, en algún momento (generalmente al cargar la aplicación o antes de cualquier solicitud de autenticación), hacer una solicitud GET al endpoint /sanctum/csrf-cookie.
    • Cuando Laravel recibe esta solicitud, hace dos cosas:
      • Establece una cookie llamada XSRF-TOKEN: Esta cookie contiene el token CSRF generado por Laravel y se envía de vuelta al navegador. Esta cookie NO es HttpOnly, lo que significa que el JavaScript de tu frontend puede leer su valor.
      • Establece la cookie de sesión de Laravel: Si aún no hay una sesión activa, se inicia una y se envía la cookie de sesión (laravel_session).
    • Importante: Debido a la Política del Mismo Origen, una página maliciosa en un dominio diferente no puede leer esta cookie XSRF-TOKEN.
  2. Paso en el Frontend: Leer la cookie y enviar el encabezado X-XSRF-TOKEN

    • Una vez que tu SPA ha recibido la cookie XSRF-TOKEN, para todas las solicitudes POST, PUT, PATCH o DELETE subsiguientes (es decir, cualquier solicitud que no sea GET, HEAD o OPTIONS), tu código JavaScript frontend debe:
      • Leer el valor de la cookie XSRF-TOKEN.
      • Enviar ese valor en un encabezado HTTP personalizado llamado X-XSRF-TOKEN (o X-CSRF-TOKEN si estás usando el nombre de encabezado tradicional de Laravel, aunque Sanctum prefiere X-XSRF-TOKEN).
    • Las librerías HTTP populares como Axios y Fetch (cuando se configura adecuadamente) a menudo se pueden configurar para hacer esto automáticamente.
  3. Paso en el Backend: Verificación del VerifyCsrfToken Middleware

    • Cuando una solicitud llega a tu API de Laravel (a una ruta protegida por el middleware web o api donde el middleware \App\Http\Middleware\VerifyCsrfToken::class está activo), este middleware realiza la verificación:
      • Compara el valor del encabezado X-XSRF-TOKEN (que envió tu frontend) con el valor de la cookie XSRF-TOKEN (que el navegador envió automáticamente).
      • Si los dos valores no coinciden, o si el encabezado X-XSRF-TOKEN está ausente en una solicitud que lo requiere, Laravel considera que es un posible ataque CSRF y rechaza la solicitud, devolviendo un error HTTP 419 (Page Expired) o 403 (Forbidden).

¿Por qué esta protección funciona contra los ataques CSRF?

  1. Imposibilidad de leer la cookie (XSRF-TOKEN) desde otro origen: Debido a la Política del Mismo Origen, un sitio web malicioso en malicious.com no puede acceder directamente a las cookies establecidas por tu dominio (tuapp.com). Por lo tanto, no puede leer el valor del XSRF-TOKEN de la cookie.
  2. Dificultad de establecer encabezados personalizados en requests cross-origin: Un sitio malicioso puede hacer que el navegador del usuario envíe una solicitud POST cross-origin. Sin embargo, no puede añadir encabezados HTTP arbitrarios como X-XSRF-TOKEN sin que el navegador primero haga una solicitud "preflight" (OPTIONS) y obtenga permiso explícito del servidor a través de los encabezados CORS. Si el servidor no permite el X-XSRF-TOKEN para ese origen malicioso, la solicitud preflight fallará y la solicitud real nunca se enviará.

De esta manera, solo tu propia SPA, que está en un dominio de primera parte (o configurado como tal en SANCTUM_STATEFUL_DOMAINS) y que puede leer la cookie XSRF-TOKEN y enviar el encabezado X-XSRF-TOKEN correctamente, podrá hacer solicitudes exitosas a tu API. Cualquier solicitud proveniente de un sitio malicioso fallará la comprobación del token CSRF.

No hay comentarios: