El decodificador Brotli de node:zlib tenía una escritura fuera de límites que permite caída, filtración de memoria o ejecución de código con datos mal formados; las versiones corregidas son 26.12.1, 24.22.1 y 22.23.4, las líneas 20, 23 y 25 no tienen corrección, y el fallo ya está en el catálogo de vulnerabilidades explotadas de CISA.
1. Qué pasó#
El 21 de octubre de 2026 el proyecto Node.js publicó versiones de seguridad para sus líneas 22, 24 y 26. La más grave de las diez vulnerabilidades corregidas es CVE-2026-111497, una escritura fuera de límites en el decodificador Brotli de node:zlib. El proyecto la califica de alta; Google, como autoridad de numeración, la puntúa con 9,2 (crítica) en CVSS v4.0, según Rapid7. La prensa la bautizó Node2Shell o Broccoli2Shell.
| Línea de Node.js | Versión corregida | Estado |
|---|---|---|
| 26.x | 26.12.1 | Con soporte |
| 24.x | 24.22.1 | Con soporte |
| 22.x | 22.23.4 | Con soporte |
| 20.x, 23.x y 25.x | Ninguna | Sin soporte: afectadas y sin corrección; hay que migrar |
No es solo teoría. Según Help Net Security, hay exploits públicos en GitHub, Imperva demostró ejecución de código contra la imagen oficial de Node.js 24.22.0 para Linux arm64 y ELLIO ve tráfico de explotación real. Rapid7 informa que CISA añadió el fallo a su catálogo de vulnerabilidades explotadas el 27 de octubre.
Documentación: Node.js: versiones de seguridad del 21 de octubre de 2026 ↗
2. Cómo funciona el fallo#
El decodificador trata un cambio de bloque inválido como una necesidad temporal de más datos y no como un error de formato permanente. Entonces sigue añadiendo datos a un búfer interno de 8 bytes y escribe más allá de sus límites. Para dispararlo, un atacante debe lograr que tu aplicación descomprima con node:zlib datos Brotli que él controla.
Esta precisión importa: la página de Node.js solo nombra node:zlib. No dice si el manejo HTTP de Content-Encoding: br ni fetch están afectados, así que no lo des por hecho. Y según Rapid7, que el decodificador exista en tu entorno no confirma la exposición: hace falta una ruta que procese datos comprimidos controlados por un atacante.
Documentación: Rapid7: CVE-2026-111497, escritura fuera de límites en el decodificador Brotli de Node.js ↗
3. A quién afecta#
Afecta a las líneas 26.x, 24.x y 22.x anteriores a las versiones corregidas. Las líneas 20.x, 23.x y 25.x, que ya no tienen soporte, también están afectadas y no recibirán corrección, según Help Net Security.
El problema nace en la biblioteca Brotli de Google, así que no es solo de Node.js. Tenable, citada por Help Net Security, incluye .NET, Bun, Envoy, Istio, clientes HTTP de Python, FFmpeg, Apache Arrow y ClickHouse. Las versiones afectadas y la exposición por defecto varían: revisa el aviso de cada proveedor. La biblioteca corregida es la 1.2.1, la 1.1.1 o la 1.0.10 según la rama.
Documentación: Help Net Security: el fallo de Brotli llega más lejos que Node.js ↗ · Node.js: calendario de versiones ↗
4. Cómo saber si estás afectado#
Primero la versión: la que corre de verdad, no la de tu terminal. En contenedores, la imagen que está desplegada. El script compara una versión con las corregidas y busca en tu código los usos del decodificador Brotli, que es la parte vulnerable.
# Versión de Node que corre de verdad: en el servidor, en el contenedor y en CI
node -v
docker ps --format "{{.Names}} {{.Image}}"
docker exec NOMBRE node -v
# Tras actualizar: reconstruir la imagen (si usas una etiqueta móvil como node:22-alpine, pide la base nueva)
docker build --pull --no-cache -t mi-app .const FIXED = { 22: [22, 23, 4], 24: [24, 22, 1], 26: [26, 12, 1] }; // primera versión corregida por línea
const parse = version => String(version).replace(/^v/, "").split(".").map(Number);
function status(version) {
const [major, minor, patch] = parse(version);
const fixed = FIXED[major];
if (!fixed) return major > 26 ? "línea más nueva que las corregidas: revisa el aviso" : "SIN CORRECCIÓN: línea sin soporte; migra a 22, 24 o 26";
const [, fixedMinor, fixedPatch] = fixed;
const ok = minor > fixedMinor || (minor === fixedMinor && patch >= fixedPatch);
return ok ? "corregida" : `VULNERABLE: actualiza a ${fixed.join(".")}`;
}
// Busca en tu código el decodificador Brotli de node:zlib (la compresión no es la parte vulnerable)
const BROTLI_DECODER = /\b(?:createBrotliDecompress|brotliDecompress(?:Sync)?)\b/g;
const usesBrotli = code => (code.match(BROTLI_DECODER) ?? []).length;
for (const v of ["v22.23.3", "v22.23.4", "v24.22.0", "v24.22.1", "v26.12.0", "v26.13.0", "v20.19.0", "v25.1.0"]) console.log(v, "->", status(v));
console.log(usesBrotli("zlib.brotliDecompressSync(body)"), usesBrotli('const { createBrotliDecompress } = require("node:zlib")'), usesBrotli("zlib.brotliCompressSync(body)"), usesBrotli("gzip"));Un resultado distinto de cero en la búsqueda no significa que tengas un problema: significa que debes revisar si esa ruta recibe datos de usuarios o de servidores que no controlas.
5. Cómo parchear#
- Actualiza a la 26.12.1, 24.22.1 o 22.23.4 según tu línea.
- Reconstruye las imágenes de contenedor desde la base nueva y vuelve a desplegar. Si usas una etiqueta móvil como node:22-alpine, fuerza la descarga (docker build --pull) y confirma la versión dentro del contenedor.
- Reinicia los servicios: un proceso que ya está corriendo sigue con la versión vieja.
- Revisa los paquetes que traen su propio Brotli: Bun, los paquetes brotli y brotlicffi de Python, .NET, Envoy o Istio, según corresponda.
- Si estás en 20.x, 23.x o 25.x, planifica la migración a una línea con soporte; no habrá corrección.
En Codifly, el sitio corre en Node.js 22.23.4, que ya incluye la corrección: lo comprobamos dentro del contenedor de producción.
6. Si no puedes parchear hoy#
Node.js no publicó ninguna solución alternativa: su única indicación es actualizar. Rapid7 sugiere, como medida temporal, restringir o desactivar las rutas de la aplicación que descomprimen Brotli de origen no confiable.
Como criterio propio, no de los avisos: revisa dónde tu aplicación acepta cuerpos o archivos comprimidos y limita quién puede enviarlos mientras actualizas.
7. Las otras nueve vulnerabilidades de la misma versión#
La versión del 21 de octubre corrige diez vulnerabilidades. La tabla las resume según la página de Node.js; si usas verificación TLS contra direcciones IP, resolución DNS con nombres de usuario o el modelo de permisos, léelas también.
| CVE | Gravedad | Componente | Qué ocurre |
|---|---|---|---|
| CVE-2026-111497 | Alta | Decodificador Brotli de node:zlib | Escritura fuera de límites con datos mal formados: caída, filtración de memoria o ejecución de código |
| CVE-2026-105482 | Alta | DNS (dns.resolve*, dns.reverse) | Los bytes NUL en nombres de host se truncan en silencio |
| CVE-2026-105483 | Alta | Verificación de host TLS | IP con dígitos de ancho completo no se reconoce como IP (26.x y 24.x) |
| CVE-2026-106069 | Alta | Verificación de host TLS | IPv4 con ceros a la izquierda se lee distinto que el resolvedor del sistema |
| CVE-2026-105481 | Media | Keep-alive del agente HTTP | Una respuesta recibida antes de enviar la petición puede aceptarse como respuesta de la nueva |
| CVE-2026-106072 | Media | Verificación de host TLS | Formas no canónicas de IPv4 permiten que un SAN DNS autentique un endpoint IP |
| CVE-2026-106074 | Media | crypto.randomUUID(), randomUUIDv7(), randomInt() | Instantáneas y SEA repiten los mismos valores en cada proceso |
| CVE-2026-106070 | Baja | zlib.ZipFile (solo 26.x) | Tras un encabezado inválido, una lectura posterior puede devolver bytes del atacante |
| CVE-2026-106071 | Baja | Modelo de permisos (solo 26.x) | Comodines como /etc/passwd.* también conceden /etc/passwd |
| CVE-2026-106073 | Baja | Modelo de permisos, fs.realpathSync() | Permite sondear rutas fuera de los directorios permitidos |
8. Mantén tus cifras al día#
Las versiones y los identificadores son los publicados hasta el 28 de octubre de 2026. La explotación avanza rápido: vuelve a revisar el aviso de Node.js y el catálogo de CISA.
- Suscríbete a la lista nodejs-sec.
- Mantén un inventario de la versión de Node.js de cada servicio y cada imagen.
- Automatiza una alerta cuando una versión quede por debajo de la corregida.
Fuentes y alcance
Documentación consultada el 28 de octubre de 2026. Los ejemplos y criterios de decisión son propuestas editoriales; adáptalos al contrato de tu aplicación y valídalos en tu entorno de pruebas autorizado.