Cuando instalás una librería de npm, no instalás una librería. Instalás una librería, más todas sus dependencias, más las dependencias de esas dependencias, más las de esas.
Un proyecto de React con dependencias razonables puede tener fácilmente 500 o 600 paquetes en node_modules. Escribiste el código de, quizás, cinco de ellos.
Los otros 595 los escribieron personas que no conocés, los mantienen con tiempo libre que a veces no tienen, y los publicaron en un registry que permite que cualquiera con una cuenta suba lo que quiera.
Eso es un vector de ataque. Y se llama supply chain attack.
Qué es un ataque de cadena de suministro
Un supply chain attack no apunta a tu código. Apunta al código en el que tu código confía.
En lugar de intentar comprometer tu aplicación directamente — lo cual requiere conocerla, encontrar una vulnerabilidad, y explotarla — el atacante va un nivel más atrás: compromete una dependencia que tu aplicación instala automáticamente.
El código malicioso llega a producción no porque alguien hackeó tu sistema, sino porque usaste npm install como siempre.
Por qué npm es particularmente vulnerable
Tres características del ecosistema npm crean condiciones favorables para este tipo de ataque:
El modelo abierto de publicación. Cualquier persona puede crear una cuenta y publicar un paquete. No hay revisión previa. La democratización que hizo crecer el ecosistema es la misma que lo hace difícil de controlar.
La profundidad de las dependencias. Un paquete que instalás directamente puede depender de diez más, cada uno de los cuales depende de otros. Un atacante no necesita comprometer una librería que usás directamente — alcanza con que esté enterrada en el árbol.
Los scripts de instalación. npm permite que los paquetes ejecuten scripts automáticamente durante la instalación (postinstall, preinstall). Código que corre en tu máquina, con tus permisos, antes de que termines de leer qué instalaste.
Tres vectores de ataque comunes
Typosquatting. Crear paquetes con nombres casi idénticos a los populares. lodash vs 1odash. express vs expres. Un error de tipeo en un npm install y estás ejecutando código malicioso.
Compromiso de cuenta del maintainer. Acceso no autorizado a la cuenta de un desarrollador legítimo para inyectar código en un paquete que ya tiene millones de descargas. No hay que engañar a nadie para que instale un paquete sospechoso — el paquete legítimo ya está instalado en todas partes.
Dependency hijacking. Introducir un paquete malicioso en una dependencia de cuarto o quinto nivel, donde nadie mira y los sistemas de auditoría raramente llegan.
Los casos reales que cambiaron cómo miramos las dependencias
Event-Stream (2018) es el caso que más cambió la conversación. Un desarrollador transfirió la propiedad del paquete a un desconocido que le ofreció ayuda con el mantenimiento. El nuevo maintainer inyectó código que apuntaba específicamente a wallets de Bitcoin. El paquete tenía millones de descargas semanales. El ataque llegó a producción en proyectos de todo el mundo antes de que alguien lo detectara.
eslint-scope (2019) comprometió la cuenta del maintainer e inyectó un script que intentaba robar variables de entorno — tokens, credenciales, API keys — de los entornos donde se ejecutaba. El npm team lo detectó rápido, pero la ventana de exposición fue real.
coa (2022) mostró que no necesitás usar un paquete directamente para estar expuesto. coa es una dependencia profunda de muchos proyectos de webpack y herramientas de build. El compromiso afectó a quien tuviera coa en cualquier nivel de su árbol de dependencias — sin saberlo.
La moraleja de los tres casos es la misma: tus dependencias son tan seguras como los paquetes en los que confían.
Cómo protegerse
La seguridad de cadena de suministro no es un problema que se resuelve de una vez. Es una práctica continua.
Auditá con frecuencia. npm audit es el punto de entrada, pero herramientas como Snyk o Dependabot dan más contexto y priorizan mejor. Integrá esto en el pipeline de CI — si una dependencia tiene una vulnerabilidad conocida, el build debería saberlo antes de que llegue a producción.
Usá lockfiles y tratálos como código. package-lock.json o yarn.lock fijan las versiones exactas de cada dependencia transitiva. Si no commiteás el lockfile, cada npm install puede traer versiones distintas. npm ci (en lugar de npm install) en CI garantiza que las versiones del lockfile son las que se instalan.
Revisá qué vas a instalar antes de instalarlo. Para paquetes nuevos, mirá el repositorio: ¿hay actividad reciente? ¿cuántos maintainers tiene? ¿el número de descargas tiene sentido para lo que hace? Un paquete con 50 estrellas y 2 millones de descargas semanales es una señal rara.
Minimizá los scripts de postinstall. Podés ver qué scripts ejecuta un paquete antes de instalarlo. Para proyectos críticos, considerá npm install --ignore-scripts y auditá qué scripts realmente necesitás habilitar.
Nunca pongas secrets en el código. Si un atacante llega a ejecutar código en tu entorno de build, lo que puede robar depende de lo que esté disponible. Variables de entorno inyectadas por el CI son más difíciles de filtrar que un .env commiteado o credenciales en archivos de configuración.
Integrá escaneo en el pipeline. No como paso opcional — como gate. Un pipeline que falla cuando hay vulnerabilidades conocidas en las dependencias hace que el equipo las tome en serio.
Formá al equipo. La mayoría de los incidentes de supply chain tienen un momento en que alguien podría haber hecho una pregunta. Un equipo que sabe qué señales buscar — un paquete que de repente cambia de maintainer, una actualización que agrega scripts de instalación nuevos, una dependencia que aparece en el lockfile sin que nadie la instalara directamente — es más difícil de engañar.
Lo que no podés eliminar, podés reducir
No existe una estrategia que haga el ecosistema npm completamente seguro. El mismo modelo abierto que lo hizo tan productivo crea una superficie de ataque que no desaparece.
Lo que sí podés hacer es reducir la probabilidad de exposición y la severidad del impacto cuando ocurre. Lockfiles, auditorías automatizadas, principio de mínimo privilegio en los entornos de build, y un equipo que entiende el riesgo son, combinados, una defensa mucho más sólida que ignorar el problema.
El npm install siguiente lo vas a hacer igual. La pregunta es si sabés lo que viene con él.