La dependency confusion es una técnica de ataque que explota cómo los gestores de paquetes resuelven dependencias internas frente a repositorios públicos. Un atacante publica un paquete malicioso en npm o PyPI con el mismo nombre que una librería privada de una empresa, y el sistema lo instala automáticamente creyendo que es una versión más reciente. Así de simple. Así de devastador. Este vector de supply chain contra desarrolladores lleva activo desde 2021, cuando Alex Birsan demostró que podía ejecutar código en redes de Apple, Microsoft y Tesla sin enviar un solo email de phishing. El npm ataque y el pypi malware asociados a esta técnica han crecido de forma sostenida: solo en 2024, la plataforma npm retiró miles de paquetes sospechosos vinculados a confusión de dependencias y typosquatting.
Cómo funciona un ataque de dependency confusion paso a paso
El mecanismo es elegante en su simplicidad. Una empresa desarrolla software con dependencias internas —paquetes privados alojados en su propio registro (Artifactory, GitHub Packages, Azure Artifacts). Estos paquetes tienen nombres como @empresa/auth-utils o internal-logger.
El atacante investiga esos nombres. A veces aparecen en repositorios públicos por error, en archivos package.json filtrados, en ofertas de empleo que mencionan el stack, o simplemente por fuerza bruta con nombres predecibles. Una vez identificado el nombre, publica en npm o PyPI un paquete homónimo con un número de versión absurdamente alto —digamos 99.0.0.
Aquí entra el fallo de diseño: muchos gestores de paquetes, por defecto, priorizan la versión más alta disponible entre todos los registros configurados. Si el registro público tiene la versión 99.0.0 y el privado tiene la 1.3.2, el sistema descarga la pública. El paquete malicioso ejecuta código arbitrario durante la instalación —scripts de preinstall en npm o setup.py en pip— y el atacante obtiene ejecución remota en la máquina del desarrollador o, peor, en el servidor de CI/CD.
Este tipo de ataque a la supply chain del desarrollador no requiere explotar ninguna vulnerabilidad de software. Explota una decisión de arquitectura. Y eso lo hace particularmente difícil de detectar con herramientas convencionales, como las que usarías para identificar un exploit zero-day.
Casos reales: de la prueba de concepto al ataque masivo
Alex Birsan publicó su investigación en febrero de 2021 a través del programa de bug bounty de varias empresas. Consiguió ejecutar código en sistemas internos de Apple, Microsoft, PayPal, Shopify, Netflix y Tesla, entre otros. Cobró más de 130.000 dólares en recompensas. Su paper demostró que el problema era sistémico, no un caso aislado.
Desde entonces, los ataques reales no han parado:
- Codecov (abril 2021): aunque técnicamente fue un ataque diferente a la supply chain, la investigación posterior reveló que varias empresas afectadas también eran vulnerables a dependency confusion en sus pipelines de CI.
- PyPI y el paquete
ctx(mayo 2022): un atacante secuestró un paquete legítimo abandonado en PyPI y lo actualizó con código que exfiltraba variables de entorno —incluyendo credenciales de AWS. La técnica combina pypi malware con abandono de paquetes, un primo hermano de la dependency confusion. - npm y paquetes corporativos (2023-2024): investigadores de seguridad de Checkmarx, Snyk y Socket documentaron campañas masivas que publicaban cientos de paquetes en npm con nombres similares a dependencias internas de empresas Fortune 500. Algunos incluían cryptominers como los que minan criptomonedas sin tu permiso.
- PyTorch nightly (diciembre 2022): el paquete
torchtritonfue comprometido mediante dependency confusion en el índice de PyPI. El equipo de PyTorch confirmó que cualquier instalación de la versión nightly entre el 25 y el 30 de diciembre ejecutó código malicioso que recopilaba datos del sistema.
En cada caso, el patrón es idéntico: un nombre de paquete interno predecible, un registro público sin protección de namespace, y un pipeline que confía ciegamente en el resultado de pip install o npm install.
npm, PyPI y otros registros: dónde está el riesgo
No todos los gestores de paquetes son igual de vulnerables. Aquí va un repaso del estado de cada ecosistema frente al npm ataque y la dependency confusion:
| Ecosistema | Riesgo de confusión | Mitigaciones nativas |
|---|---|---|
| npm | Medio-alto | Scoped packages (@org/pkg) reducen el riesgo. Pero paquetes sin scope son vulnerables si el registro privado no está bien configurado. |
| PyPI / pip | Alto | No tiene namespaces ni scopes nativos. Cualquiera puede publicar cualquier nombre. --index-url vs --extra-index-url es la diferencia entre seguro e inseguro. |
| RubyGems | Medio | Sin namespaces oficiales, pero menor adopción de registros privados corporativos. |
| NuGet (.NET) | Medio | Soporta package source mapping desde 2022, que vincula paquetes a fuentes específicas. |
| Go modules | Bajo | Los módulos usan rutas de dominio (github.com/empresa/pkg), lo que dificulta la confusión. |
Python es, con diferencia, el ecosistema más expuesto. La diferencia entre usar pip install --index-url https://mi-registro-privado/simple/ paquete y pip install --extra-index-url https://mi-registro-privado/simple/ paquete es crítica. Con --extra-index-url, pip busca en ambos registros y elige la versión más alta. Con --index-url, solo busca en el privado. Un flag. Una letra. La diferencia entre una instalación segura y pypi malware ejecutándose en tu pipeline.
Cómo protegerte: defensas concretas que funcionan
La buena noticia: este ataque tiene mitigaciones claras. La mala: requieren disciplina y configuración explícita que muchos equipos no implementan.
Para proyectos npm
- Usa scoped packages: publica tus paquetes internos bajo un scope de organización (
@tuempresa/paquete). Nadie puede publicar bajo tu scope en el registro público sin control de tu org. - Configura
.npmrccorrectamente: apunta el scope a tu registro privado con@tuempresa:registry=https://tu-registro-privado/. - Usa
package-lock.jsony verifica integridad: el lockfile fija versiones y hashes. Si alguien cambia el contenido del paquete, la instalación falla.
Para proyectos Python
- Nunca uses
--extra-index-urlen producción. Usa--index-urlapuntando a un registro privado que haga proxy de PyPI con reglas de prioridad. - Reserva nombres en PyPI: si tienes paquetes internos, publica un placeholder vacío en PyPI con ese nombre. Es feo, pero funciona.
- Usa herramientas de verificación: pip-audit y Safety detectan paquetes con vulnerabilidades conocidas. Socket.dev analiza comportamiento sospechoso en paquetes nuevos.
Para cualquier ecosistema
- Audita tus dependencias: ejecuta
npm audit,pip-audito equivalentes en cada build. Integra Snyk, Dependabot o Renovate en tu CI/CD. - Monitoriza nombres de paquetes: servicios como Socket.dev y Phylum alertan cuando aparece un paquete público con nombre similar a tus dependencias internas.
- Pinea versiones en lockfiles: nunca dependas de rangos abiertos (
^1.0.0) en producción. Fija la versión exacta y el hash. - Revisa scripts de instalación: antes de añadir una dependencia nueva, mira qué hace su
preinstall,postinstallosetup.py. Si exfiltra variables de entorno o hace llamadas HTTP durante la instalación, es una señal de alarma que deberías verificar con la misma cautela que un enlace sospechoso.
Para equipos que gestionan infraestructura propia, como quienes configuran herramientas de automatización o desarrollo web, esta higiene de dependencias debería ser parte del checklist de despliegue.
Preguntas frecuentes
¿Puedo ser víctima de dependency confusion si solo uso paquetes públicos?
El riesgo principal afecta a organizaciones que mantienen paquetes internos privados. Pero el typosquatting —publicar requets en lugar de requests— es un ataque relacionado que afecta a cualquiera. Revisa siempre el nombre exacto del paquete antes de instalarlo.
¿Cómo sé si alguno de mis paquetes instalados es malicioso?
Ejecuta npm audit o pip-audit para comprobar vulnerabilidades conocidas. Para análisis más profundo, Socket.dev inspecciona el comportamiento del paquete (llamadas de red, acceso al sistema de archivos, ejecución de comandos) y alerta sobre actividad sospechosa. Si sospechas que ya has instalado algo raro, revisa las señales de malware en tu equipo.
¿Los lockfiles me protegen completamente?
Los lockfiles protegen contra cambios inesperados en dependencias ya resueltas. Pero no te protegen en el momento de la resolución inicial. Si la primera vez que generas el lockfile tu sistema ya resolvió el paquete malicioso, quedarás fijado a esa versión comprometida. La protección real viene de configurar correctamente las fuentes de paquetes.
¿GitHub y npm están haciendo algo para mitigar esto?
npm introdujo la verificación de provenance con Sigstore en 2023, que permite verificar que un paquete fue construido en un pipeline de CI conocido. PyPI implementó Trusted Publishers ese mismo año, vinculando publicaciones a flujos de GitHub Actions verificados. Ambas medidas reducen el riesgo, pero requieren adopción activa por parte de los mantenedores.
El siguiente paso
Abre ahora mismo el archivo de configuración de tu gestor de paquetes —.npmrc, pip.conf, pyproject.toml— y comprueba cómo resuelve las dependencias privadas frente a las públicas. Si ves --extra-index-url en un proyecto Python o un registro privado sin scope en npm, ya tienes tu primera tarea de seguridad para esta semana. Un cambio de configuración de cinco minutos puede ahorrarte un incidente de supply chain que ningún antivirus va a detectar.


