Deshabilitar CSRF en Spring Security con JWT

Resumen

Si estás construyendo una API REST stateless con Spring Security y autenticación por JSON Web Tokens, vas a chocar con un problema: cualquier petición POST, PUT o DELETE te devuelve un 401, aunque tu usuario y contraseña sean válidos. La razón está en el CSRF filter, una protección que viene activa por defecto y que en ciertos escenarios necesitas deshabilitar.

Antes de tocar configuración, vale la pena entender qué es ese ataque que Spring intenta bloquear y por qué, en una arquitectura basada en tokens, deja de tener sentido mantenerlo activo.

Qué es un ataque CSRF y cómo funciona

Un Cross Site Request Forgery es un ataque que aprovecha una sesión activa para falsificar peticiones haciéndose pasar por un usuario autorizado. El servidor recibe la petición maliciosa exactamente igual que la legítima y no puede distinguirlas.

Imagina que inicias sesión en tu banco para hacer una transferencia. El formulario tiene un método POST, un action hacia /transfer y dos inputs: el monto y la cuenta destino. Sin cerrar esa pestaña, abres otro sitio que muestra un botón llamativo: Show Frenchie's videos. Le das clic.

Lo que no ves es que detrás del botón hay un form oculto que apunta al mismo /transfer de tu banco, con un monto de mil dólares y la cuenta del atacante en campos hidden. Como tu sesión sigue abierta, el servidor procesa la transferencia sin sospechar nada.

¿Por qué el servidor no detecta el ataque CSRF? Porque las dos peticiones son idénticas a nivel HTTP. El servidor no tiene cómo saber si el clic vino del sitio oficial del banco o de un sitio malicioso que aprovecha la sesión activa.

Cómo se previene un CSRF con un token aleatorio

La defensa clásica consiste en añadir un input oculto con un código aleatorio único, el llamado CSRF token, que el servidor genera y verifica en cada petición sensible.

El sitio oficial conoce ese token porque el servidor se lo entregó al renderizar el formulario. El sitio malicioso no puede adivinarlo, así que cualquier petición forjada llega sin token o con uno inválido y se bloquea.

Spring Security trae este mecanismo activado por defecto a través del CSRF filter, que verás listado dentro del filter chain cuando lances la aplicación.

Por qué deshabilitar CSRF en una API REST con JWT

La pregunta lógica es: si la protección es tan buena, ¿por qué desactivarla? La respuesta tiene que ver con el tipo de aplicación que estás construyendo.

Una API REST stateless no almacena sesiones en el servidor. Cada petición se autentica de forma independiente con un JSON Web Token enviado en el header Authorization. Sin sesión persistente, el vector clásico del CSRF deja de existir, porque el atacante no puede aprovechar una cookie de sesión que el navegador adjunte automáticamente.

¿Cuándo es seguro deshabilitar CSRF en Spring Security? Cuando tu API es stateless, autenticas con JWT y envías el token en el header Authorization en cada petición. Nunca cuando guardas el JWT en una cookie que el servidor lea de forma automática.

Ese matiz es importante. Si almacenas el JSON Web Token dentro de una cookie y el backend lo extrae desde allí, vuelves a quedar expuesto a CSRF, porque el navegador enviará la cookie sin que el usuario lo pida. La regla es clara: el token siempre viaja en el header Authorization.

Cómo deshabilitar el CSRF filter en Spring Security

Cuando lanzas la aplicación con la configuración por defecto, dentro del filter chain aparece el CSRF filter. Si abres Postman, autenticas con Basic Auth y haces un GET, todo funciona y recibes un 200.

Pero si intentas un PUT para actualizar el precio de una pizza a 23.9, el servidor responde con un 401, aunque las credenciales sean correctas. Ese rechazo viene precisamente del filtro CSRF, que protege los métodos POST, PUT y DELETE.

La desactivación se hace en una sola línea dentro de la configuración del HttpSecurity:

java http.csrf().disable();

Al reiniciar la aplicación y revisar el filter chain en el log, el CSRF filter desaparece. Si buscas la palabra CSRF en los logs, obtendrás cero resultados.

Cómo verificar el cambio en Postman

Para confirmar que la configuración funcionó, sigue estos pasos:

  1. Copia la contraseña generada por Spring Security al iniciar la app.
  2. Pégala en la pestaña Authorization de Postman como autenticación Basic.
  3. Lanza la petición PUT hacia la pizza con id 1, enviando el nuevo precio en el body.
  4. Revisa que ahora la respuesta sea un status 200 y el precio quede actualizado.

Con CSRF deshabilitado, los métodos POST, PUT y DELETE funcionan sin que el filtro intercepte la petición.

Qué recordar antes de avanzar

Deshabilitar CSRF no es una mala práctica por sí sola, lo es solo cuando se hace en el contexto equivocado. Tres ideas para no perder el rastro:

  • Solo aplica esta configuración en APIs stateless con JWT en header.
  • Nunca guardes el JSON Web Token en cookies leídas automáticamente por el servidor.
  • Mantén la protección activa en cualquier app web tradicional con sesiones y formularios.

El siguiente paso natural es configurar CORS para que la API acepte peticiones desde orígenes distintos, algo común en arquitecturas modernas con frontends desacoplados. ¿Ya tuviste que lidiar con un 401 inesperado por CSRF? Cuéntalo en los comentarios.