← Volver a artículos
Seguridad· 2 min de lectura

CVE-2026-22732: Spring Security deja de escribir las cabeceras HTTP de seguridad en silencio

Spring Security se encarga, entre otras cosas, de añadir las cabeceras de respuesta que blindan una aplicación web: Cache-Control, X-Frame-Options, Strict-Transport-Security, Content-Security-Policy. El problema de CVE-2026-22732 es que, bajo ciertas condiciones, esas cabeceras nunca llegan al navegador. Y lo hace sin lanzar ningún error, así que la aplicación parece estar protegida cuando no lo está.

El equipo de Spring (VMware/Broadcom) le ha asignado un CVSS de 9.1, gravedad crítica. El vector es de red, complejidad baja, sin privilegios ni interacción del usuario.

Qué pasa por dentro

Spring Security escribe sus cabeceras de seguridad de forma “perezosa” (lazy) por defecto. Es decir, las inyecta tarde en el ciclo de la respuesta, a través de su HeaderWriterFilter. El problema aparece cuando el controlador compromete la respuesta antes de tiempo: si escribes directamente en response.getOutputStream(), llamas a response.flushBuffer() o fijas la longitud con setIntHeader("Content-Length", ...), la respuesta se da por enviada (committed) antes de que Spring Security tenga ocasión de añadir sus cabeceras. Resultado: el navegador recibe la página sin las protecciones esperadas.

No es un caso raro. Cualquier endpoint que devuelva contenido escribiendo en el stream de salida (descargas de ficheros, generación de PDF, streaming de datos) puede caer en esta situación.

A quién afecta

Aplicaciones servlet que usen Spring Security con escritura perezosa de cabeceras (el comportamiento por defecto). Las ramas afectadas son:

  • 5.7.0 a 5.7.21
  • 5.8.0 a 5.8.23
  • 6.3.0 a 6.3.14
  • 6.4.0 a 6.4.14
  • 6.5.0 a 6.5.8
  • 7.0.0 a 7.0.3

Las aplicaciones reactivas (WebFlux) no están en el mismo supuesto; el problema es específico del modelo servlet.

Por qué importa

Que falte Content-Security-Policy o X-Frame-Options no tumba el servidor, pero deja la puerta abierta a clickjacking, a que datos sensibles acaben en una caché compartida por culpa de un Cache-Control ausente, o a degradar las protecciones de transporte si no se envía Strict-Transport-Security. Lo peor es el carácter silencioso: tu configuración de seguridad sigue ahí, parece correcta en el código, pero no surte efecto en la respuesta. Una auditoría que solo mire el código fuente no detecta el hueco; hay que inspeccionar las cabeceras reales que recibe el cliente.

Mitigación

Actualiza a una versión corregida: 5.7.22 o superior, 5.8.24 o superior, 6.3.15 o superior, 6.4.15 o superior, 6.5.9 o superior, o 7.0.4 o superior.

Si no puedes actualizar de inmediato, hay un workaround: forzar la escritura temprana de las cabeceras poniendo la propiedad HeaderWriterFilter.shouldWriteHeadersEagerly a true. Con eso las cabeceras se añaden antes de que el controlador pueda comprometer la respuesta.

Sea cual sea el camino, conviene verificar las cabeceras de las respuestas reales (con curl -I o las herramientas de desarrollo del navegador) en los endpoints que escriben directamente en el stream, que son los que más papeletas tienen de haber estado expuestos.

Fuente