Dependency confusion: ataques a desarrolladores a través de paquetes npm y PyPI

Dependency confusion: ataques a desarrolladores a través de paquetes npm y PyPI

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 torchtriton fue 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:

EcosistemaRiesgo de confusiónMitigaciones nativas
npmMedio-altoScoped packages (@org/pkg) reducen el riesgo. Pero paquetes sin scope son vulnerables si el registro privado no está bien configurado.
PyPI / pipAltoNo tiene namespaces ni scopes nativos. Cualquiera puede publicar cualquier nombre. --index-url vs --extra-index-url es la diferencia entre seguro e inseguro.
RubyGemsMedioSin namespaces oficiales, pero menor adopción de registros privados corporativos.
NuGet (.NET)MedioSoporta package source mapping desde 2022, que vincula paquetes a fuentes específicas.
Go modulesBajoLos 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

  1. 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.
  2. Configura .npmrc correctamente: apunta el scope a tu registro privado con @tuempresa:registry=https://tu-registro-privado/.
  3. Usa package-lock.json y verifica integridad: el lockfile fija versiones y hashes. Si alguien cambia el contenido del paquete, la instalación falla.

Para proyectos Python

  1. Nunca uses --extra-index-url en producción. Usa --index-url apuntando a un registro privado que haga proxy de PyPI con reglas de prioridad.
  2. Reserva nombres en PyPI: si tienes paquetes internos, publica un placeholder vacío en PyPI con ese nombre. Es feo, pero funciona.
  3. 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-audit o 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, postinstall o setup.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.

dependency confusion paquete malicioso npm ataque pypi malware supply chain desarrollador

Artículos relacionados

← Volver al blog