ChainDrop: el ataque a npm que llegó con procedencia válida
ChainDrop comprometió cientos de paquetes npm con procedencia válida y robó credenciales de nube y CI. Qué pasó, por qué la firma no bastó y cómo responder.
El 4 de agosto de 2026 empezó uno de los ataques a la cadena de suministro de npm más amplios del año. Se le conoce como ChainDrop, es una variante de Shai-Hulud y no explotó ninguna vulnerabilidad en el código: tomó el control de la cuenta de GitHub de un mantenedor y publicó versiones maliciosas de paquetes que muchísimos proyectos usan sin saberlo.
Lo que lo hace especialmente incómodo para los equipos que ya habían “hecho la tarea” es que esas versiones tenían procedencia válida. Pasaban la verificación que muchos consideraban suficiente.
Este artículo explica qué pasó, por qué la procedencia no bastó y qué debería hacer tu equipo, tanto si resultó afectado como si no.
Qué pasó
El atacante tomó la cuenta de GitHub del mantenedor de keyv, hizo push directamente a la rama principal y generó releases. Como la publicación pasaba por GitHub Actions, las versiones maliciosas salieron con procedencia firmada por ese mismo flujo.
La propagación fue rápida. Según la actualización de Aikido del 5 de agosto, al menos 444 paquetes (en 1381 versiones) estaban comprometidos, con más de 2.000 millones de instalaciones mensuales en conjunto.
El 6 de agosto, la Agencia de Ciberseguridad de Singapur (CSA) publicó el aviso AD-2026-009 con las versiones maliciosas identificadas. Entre ellas:
| Paquete | Versión maliciosa |
|---|---|
| keyv | 6.0.0 |
| flat-cache | 6.1.24 |
| file-entry-cache | 11.1.6 |
| cacheable-request | 13.0.20 |
| cacheable | 2.5.1 |
| cache-manager | 7.2.10 |
Esta tabla no es la lista completa; revisa las fuentes al final del artículo para el detalle.
El dato que debería preocupar a cualquier equipo JavaScript: flat-cache y file-entry-cache son dependencias de herramientas comunes como ESLint. No hacía falta usar keyv directamente para estar expuesto; bastaba con tener un linter en el proyecto o en el pipeline.
Qué hacía el código malicioso
El payload se ejecutaba mediante un hook preinstall, es decir, durante la instalación de dependencias, antes de que nadie importara el paquete. Su objetivo eran las credenciales:
- Tokens de npm y de GitHub, incluidos tokens OIDC.
- Credenciales de AWS, Azure y GCP.
- Credenciales de Kubernetes y de Vault.
- Claves de API de herramientas de IA.
Piensa dónde se ejecuta npm install: en laptops de desarrolladores con acceso a la nube y en runners de CI/CD con permisos para desplegar a producción. Ese es exactamente el botín.
Por qué la procedencia válida no fue suficiente
La procedencia (provenance) responde una pregunta concreta: ¿este paquete se construyó a partir de este repositorio y este flujo de CI? Es una defensa útil contra paquetes publicados desde la máquina de un atacante con un token robado.
Pero no responde otra pregunta: ¿el código de ese repositorio es legítimo? Si alguien controla la cuenta del mantenedor, puede hacer push a la rama principal y dejar que el pipeline oficial firme el resultado. La firma es auténtica; el contenido, no.
La lección no es abandonar la procedencia, sino dejar de tratarla como sinónimo de confianza. Es una capa más, que necesita acompañarse de controles sobre qué se ejecuta al instalar, qué secretos están al alcance y cómo se protegen las cuentas que publican.
Respuesta inmediata: checklist si pudiste estar expuesto
Las recomendaciones de la CSA son directas: eliminar las versiones afectadas de los entornos de desarrollo y de CI/CD, rotar todas las credenciales y tratar los equipos afectados como comprometidos. Traducido a tareas:
- Busca en todos los lockfiles de tus repositorios las versiones listadas en los avisos.
- Identifica qué laptops y runners ejecutaron una instalación con esas versiones, y desde cuándo.
- Elimina las versiones afectadas, fija versiones conocidas como seguras y regenera el lockfile.
- Limpia la caché de npm, las cachés de CI y las imágenes de contenedor construidas con esas dependencias.
- Trata los equipos y runners afectados como comprometidos: aíslalos y reinstálalos en lugar de “limpiarlos”.
- Rota todas las credenciales que estuvieron a su alcance: npm, GitHub, AWS, Azure, GCP, Kubernetes, Vault y claves de API de IA.
- Revisa los registros de auditoría de GitHub, de tus nubes y de npm en busca de actividad inusual desde el 4 de agosto.
Si tienes dudas sobre si un equipo estuvo expuesto, asume que sí. Rotar una credencial de más cuesta mucho menos que dejar activa una comprometida.
Prevención: lo que cambia con npm 12
Un mes antes del ataque, el 8 de julio de 2026, npm 12 llegó a disponibilidad general con cambios que apuntan justo a este tipo de amenaza:
- Scripts de ciclo de vida bloqueados por defecto. Los scripts
preinstall,installypostinstallde las dependencias, así como las compilaciones implícitas con node-gyp, ya no se ejecutan salvo que se permitan explícitamente. - Aprobación explícita. Se aprueban con
npm approve-scriptsy la lista de permitidos se guarda enpackage.json, lo que la hace visible en la revisión de código. - Dependencias de git y de URL remota. Requieren
--allow-gity--allow-remote, respectivamente. - Tokens granulares con bypass de 2FA. Desde principios de agosto de 2026 ya no pueden hacer operaciones sensibles de gestión de cuentas y paquetes, y hacia enero de 2027 perderán la capacidad de publicar directamente.
Un payload que depende de un preinstall no se ejecuta con npm 12 si ese paquete no tiene scripts aprobados. Eso no saca el paquete malicioso de tu árbol de dependencias, pero cierra la puerta que usó este ataque.
Checklist de prevención para equipos de desarrollo
| Control | Por qué importa |
|---|---|
| Actualizar a npm 12 en laptops y CI | Bloquea por defecto los scripts de instalación de dependencias |
| Revisar cada cambio en la lista de scripts aprobados | La allowlist en package.json es código: debe pasar por pull request |
Usar npm ci con el lockfile versionado |
Instalaciones reproducibles, sin versiones nuevas sorpresa |
| No adoptar versiones recién publicadas de inmediato | Da tiempo a que se detecten compromisos |
| Mínimo privilegio en CI | El paso de instalación no debería ver secretos de despliegue |
| Runners efímeros | Un runner comprometido no sobrevive al siguiente job |
| Protección de ramas y 2FA fuerte en cuentas que publican | Dificulta el push directo a la rama principal |
| Dejar de publicar con tokens con bypass de 2FA | npm los está retirando |
| Tratar las claves de API de IA como secretos de producción | También fueron objetivo |
Preguntas frecuentes
No usamos keyv. ¿Estamos a salvo?
No necesariamente. flat-cache y file-entry-cache son dependencias de herramientas comunes como ESLint. Revisa el lockfile completo, no solo tus dependencias directas.
¿npm 12 nos habría protegido?
Habría impedido que el preinstall de una dependencia se ejecutara sin aprobación explícita, que fue el mecanismo de este payload. Aun así, hay que eliminar las versiones afectadas y aplicar el resto de controles.
¿Basta con actualizar los paquetes?
No. Si una versión maliciosa llegó a instalarse, las credenciales de ese equipo deben considerarse robadas. Actualizar sin rotar credenciales deja intacto el acceso del atacante.
Conclusión
ChainDrop demostró que una firma válida solo prueba de dónde vino un paquete, no que sea seguro. La defensa real es por capas: controlar qué se ejecuta al instalar, reducir los secretos al alcance y proteger las cuentas que publican. Si tu equipo quiere revisar su pipeline o priorizar estos cambios con apoyo externo, puedes escribirnos.
Fuentes
¿Tienes un proyecto en mente?
Conversemos sobre cómo podemos ayudarte a alcanzar tus objetivos tecnológicos.
Agenda una consulta gratuita