Ir al contenido principal

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.

Comentarios

Entradas populares de este blog

Crear proyecto de frontend con Vite, React, Tailwind, Typescript y JSX

 ¿Cómo se crea un proyecto exclusivo de frontend con Vite, React, Tailwind, Typescript y JSX? ¡Absolutamente! Entiendo que quieres la guía completa para pasar de cero a tener un proyecto de frontend moderno, vacío y listo para ser llenado con tu código. A continuación, te proporciono un archivo de guía detallada que incluye todos los comandos necesarios para inicializar el proyecto, configurar Tailwind CSS, y los pasos clave para empezar a "poblarlo". Voy a generar un documento Markdown con la guía paso a paso, incluyendo los comandos de terminal y las modificaciones esenciales a los archivos de configuración. Guía: Creación de un Proyecto Frontend Puro (Vite, React, TypeScript, Tailwind) Este documento detalla el proceso para inicializar un proyecto de frontend vacío y optimizado, utilizando Vite como empaquetador, React como librería, TypeScript para tipado y Tailwind CSS para estilizado. 1. Inicialización del Proyecto con Vite Vite es la herramienta más rápida para emp...

Tokens V2

Tokens: Programme to discover tokens, where there are not. Now available at  https://puszcza.gnu.org.ua/projects/tokens/ This is Version 2, for  version 1, go here . Synopsis: use TokensV2; sub printFile; my @FORMAT = ( ['<Message Date=".*?" Time=".*?" DateTime=".*?" SessionID=".*?"><From>(?:<User FriendlyName=".*?"/>)+</From><To>(?:<User FriendlyName=".*?"/>)+</To><Text(?: Style=".*?")?>.*?</Text></Message>',   sub {     my $fh = $_[1];     my ($d, $t, $f, $s, $T) = $_[0] =~ m|<Message Date="(.*?)" Time="(.*?)" DateTime=".*?" SessionID=".*?">(<From>(?:<User FriendlyName=".*?"/>)+</From>)<To>(?:<User FriendlyName=".*?"/>)+</To><Text(?: Style="(.*?)")?>(.*?)</Text></Message>|;     my $F = join '<br />', ...

Perl Net::LDAP::SimpleServer

Adaptaciones sobre el módulo LDAP Server para Windows (Strawberry Perl) Lista de adaptaciones (continúa más abajo): - Relajación de condiciones de bind:     - Cuenta principal (principal account)     - Validación de contraseñas Ubicación del archivo: %Strawberry_Perl%\site\lib\net\ldap\SimpleServer\ProtocolHandler.pm CPAN: http://search.cpan.org/~russoz/Net-LDAP-SimpleServer-0.0.17/lib/Net/LDAP/SimpleServer.pm Código: package Net::LDAP::SimpleServer::ProtocolHandler; use strict; use warnings; # ABSTRACT: LDAP protocol handler used with Net::LDAP::SimpleServer our $VERSION = '0.0.17';    # VERSION use Net::LDAP::Server; use base 'Net::LDAP::Server'; use fields qw(store root_dn root_pw allow_anon); use Carp; use Net::LDAP::LDIF; use Net::LDAP::Util qw{canonical_dn}; use Net::LDAP::FilterMatch; use Net::LDAP::Constant (     qw/LDAP_SUCCESS LDAP_AUTH_UNKNOWN LDAP_INVALID_CREDENTIALS/,   ...