Ataque de confusión de dependencias: cómo funciona y cómo prevenirlo

Ya están disponibles los parches de php2shell (CVE-2026-109618 y CVE-2026-109619) para PHP en fin de vida útil. Dado que las respuestas del departamento de ventas están tardando más de lo habitual debido a la elevada demanda, la forma más rápida de obtener los paquetes parcheados es comprar ELS para Runtimes en línea (se abre en una pestaña nueva). Lee las preguntas frecuentes sobre los parches

Ir directamente al resumen generado por IA de esta entrada del blog

  • Resume esta entrada con Perplexity
  • Resume esta publicación con Gemini

En el desarrollo de software moderno, hemos pasado de un modelo de instalación basado estrictamente en la cadena de suministro a un modelo híbrido. Ahora las dependencias pueden obtenerse a través de dos vías: paquetes privados y públicos. Este artículo se centrará en la confusión de dependencias y en cómo se relaciona con los ataques de dependencia transitiva profunda.

El modelo híbrido de cadena de suministro

 

Today, we have two different ways of installing packages via various tools like npm, pnpm, etc. The concept of a single way to install packages is a thing of the past. Years ago, we would just open our terminal up and type npm install <package>. It would get pulled from the npm registry, and that would be that. Private packages, or scoped packages, can be delivered from the npm registry and are secure by default. You can also host scoped packages in internal registries like Nexus, Artifactory, and Verdaccio.

Un paquete con ámbito tiene este aspecto: @myorg/mypackage. Comienza con el símbolo «@» seguido del nombre de tu organización, y termina con una «/» y el nombre del paquete. Se trata de un paquete privado y seguro alojado en el registro de npm. Esto también te ofrece la posibilidad de agrupar paquetes dentro de tu organización. También puedes instalar paquetes públicos que sean de código abierto. Es casi seguro que vas a instalar un paquete público para desarrollar funcionalidades. Por lo tanto, tendrás una combinación de paquetes públicos y privados.

La vulnerabilidad se cuela sigilosamente cuando se utilizan paquetes públicos de código abierto o se tiene una configuración inadecuada de CI/CD. Los gestores de paquetes se diseñaron para facilitar las cosas. Esa comodidad tiene un precio. Esos gestores de paquetes dan prioridad a los números de versión más altos frente al origen del código. Y entonces la situación empieza a agravarse.

 

¿Qué es un ataque de confusión de dependencias?

 

Los ataques de confusión de dependencias se aprovechan de la forma en que los gestores de paquetes modernos resuelven las dependencias entre múltiples fuentes. En entornos en los que se utilizan tanto registros privados como públicos, este proceso de resolución puede generar riesgos inesperados.

Lo que parece ser una instalación rutinaria de dependencias puede convertirse, en cambio, en una puerta de entrada para código malicioso, especialmente cuando los paquetes internos no están debidamente aislados o delimitados.

Cómo funciona la confusión de dependencias

 

Un ataque de confusión de dependencias (o confusión de espacios de nombres) aprovecha una vulnerabilidad en la forma en que los gestores de paquetes resuelven las dependencias entre varios registros. Se produce cuando un sistema de compilación está configurado para obtener paquetes tanto de un registro privado (interno) como de un registro público (como npmjs.com).

Si un atacante identifica el nombre de un paquete privado sin ámbito y publica un paquete malicioso con exactamente el mismo nombre en el registro público utilizando un número de versión superior, el gestor de paquetes considerará que la versión pública maliciosa es la «mejor coincidencia» y la introducirá silenciosamente en la cadena de suministro. Esto ocurre porque los gestores de paquetes dan prioridad a las versiones superiores cuando hay nombres coincidentes.

En la práctica, los gestores de paquetes dan prioridad a la versión más alta que cumple con el rango , tal y como se ha indicado anteriormente. Si tu package.json pide ^1.2.0 de internal-tool, y el atacante publica la v99.0.0 en npmjs.com, el administrador asume que la versión pública es una actualización legítima y la descarga.

Otra situación habitual se da cuando una organización no utiliza paquetes con ámbito. El paquete se publicó en el registro privado como auth-package, pero también hay un paquete con el mismo nombre en el registro público. El registro recuperará el que tenga el número de versión más alto, a menos que se especifique lo contrario en el .npmrc .

Desglose de la dependencia transitiva

 

Una dependencia transitiva es un paquete que tú, como desarrollador, no has instalado intencionadamente, pero que se instala automáticamente al instalar una dependencia directa de un proyecto. Las dependencias transitivas pueden estar ocultas a varios niveles de profundidad en el árbol de dependencias, lo que dificulta identificar el origen de los fallos de seguridad.

«La mayoría de los incidentes de ciberseguridad estaban relacionados con parches no instalados. El 60,4 % de los incidentes se produjo cuando había un parche disponible, pero no se había instalado».

 

Mantenerse a la vanguardia y aplicar los parches en el momento oportuno es fundamental para combatir las vulnerabilidades de seguridad relacionadas con las dependencias transitivas en la cadena de suministro de software. A continuación se muestra un ejemplo de cómo es una dependencia transitiva.

rxjs es una dependencia directa de Angular v20, pero si te fijas en el directorio carpeta node_modules y encontrar el archivo rxjs , verás dentro del archivo package.json el tslib . Este paquete no lo instalaste tú, sino que vino incluido en el rxjs , dentro del proyecto Angular v20.

Anatomía de un ataque de confusión de dependencias

 

Tomemos como ejemplo un componente básico de la interfaz de usuario que quieras instalar en un proyecto: un botón sencillo. Este botón procede directamente del sistema de diseño de tu organización y se encuentra en una biblioteca de componentes.

No tiene mucho misterio. Un fondo, una fuente con texto y esquinas redondeadas. Este botón es tal y como viene definido en el sistema de diseño. El código está escrito en React y tiene este aspecto:

 

export const DemoButton = () => {

  return (

    <button style={{ backgroundColor: 'blue', color: 'white' }}>

      Secure Scoped Action

    </button>

  );

};

 

Lo instalamos a través de npm, lo importamos a nuestro proyecto y ya está. Un botón que funciona en nuestra interfaz de usuario. Por otro lado, sin embargo, hay otro botón sofisticado por ahí, y ese botón o bien no está restringido al registro privado, o bien sí lo está, pero un desarrollador instala el que está disponible públicamente. El que está disponible públicamente es el malicioso que activa alertas en la página de inicio, intercepta credenciales en segundo plano o invierte los colores del sitio, ya sea de inmediato o tras un tiempo determinado.

El código de este botón tiene este aspecto, aunque quizá no lo hayas revisado:



import React, { useEffect } from 'react';



export const DemoButton = () => {

  useEffect(() => {

    document.body.style.filter = 'invert(100%)';

    alert("Hacked via Unscoped Dependency!");

  }, []);




  return <button>Click for Update</button>;

};

 

Lo más probable es que este botón no se incluya como una dependencia directa. En el código de ejemplo, lo muestro como una dependencia directa porque no dispongo de un sistema de diseño completo con múltiples componentes. Si instalas una biblioteca de interfaz de usuario no verificada o un paquete que sirva de dependencia para tu sistema de diseño, podría introducirse un botón malicioso de forma transitiva.

Tanto si se introducen de forma directa como transitiva, estas dependencias pasan a formar parte del perímetro de confianza de tu aplicación y, si no se controlan, pueden convertirse en una superficie de ataque potencial.

Ataques previos y posteriores a la instalación

 

Los atacantes utilizan pre y postinstalación para ejecutar cargas maliciosas antes de que el desarrollador tenga tiempo siquiera de ejecutar el código. La forma en que un script de preinstalación consiste en que, antes de que se coloquen los archivos del paquete en tu carpeta, el script puede realizar comprobaciones del entorno para determinar si debe ejecutar un comportamiento malicioso. Si el script detecta un entorno reforzado o supervisado, se comunica con su servidor de forma silenciosa y permanece inactivo para evitar ser detectado.

La script espera a que se abra la puerta y, a continuación, pueden suceder un montón de cosas. Un script puede robar información confidencial o, en palabras de E.T., llamar a casa y avisar al servidor de que la puerta está abierta de par en par y cualquiera puede entrar. Se puede comparar con la tradición de los vampiros. Una vez que invitas a un vampiro a entrar, se abre la veda.

Una vez que el script esté dentro de tu node_modules, ya es demasiado tarde: se ejecuta inmediatamente. El script ejecuta un comando como curl o wget para enviar tu .env , las claves SSH o los tokens de AWS al sitio del atacante. Una vez que este disponga de estas credenciales, no hay mucho que puedas hacer, salvo eliminar tus claves y tokens y generar otros nuevos.

Solución de la confusión de dependencias

 

Hay varias formas de adelantarnos a estos problemas y lograr que el proyecto sea seguro y esté actualizado. 

Paquetes con ámbito

La forma más clara de evitar un ataque de confusión de dependencias es registrar tu organización en el gestor de paquetes y utilizar paquetes con ámbito, @myorg/mypackage. Esto proporciona un paquete seguro y privado que se puede utilizar en tus propios proyectos en toda la organización.

Correcto .npmrc Configuraciones

Al crear tu .npmrc , asegúrate de asignar estrictamente los ámbitos específicos a los registros privados. Al garantizar que se asignan estrictamente, se evitará que se «filtren» al registro público. Un ejemplo de un .npmrc configurado para ello es el siguiente, y es la forma más eficaz de combatir los problemas dentro de tu proyecto:

 

# Establecer el registro predeterminado para los paquetes públicos

registry=https://registry.npmjs.org/




# Forzar que todos los paquetes con ámbito @org se resuelvan ÚNICAMENTE a través del registro privado

@org:registry=https://npm.pkg.github.com/

always-auth=true

Uso de –ignore-scripts

Another clever way to get away from the preinstall and postinstall scripts is to use –ignore-scripts when installing packages. Using that flag looks like this: npm install <new-package> –ignore-scripts. This isn’t just something that is used once; you want to bake this into your automation and workflows as well.

Integridad del archivo de bloqueo

Tu package-lock.json o yarn.lock hace algo más que fijar versiones; almacena un hash, un hash criptográfico. Si el hash cambia y la compilación falla, eso te indica que un atacante ha sustituido el paquete en el registro. Usando npm ci se aplica el archivo de bloqueo. Con npm install, esta protección se puede eludir.

Asegura la pila

Teniendo en cuenta todo esto, asegúrate de seguir una lista de comprobación de seguridad rigurosa. Estos puntos son solo una parte de ella.

 

  1. Utiliza paquetes con ámbito y asegúrate de registrar tu organización en el registro
  2. Configura tu .npmrc para asignar estrictamente tus paquetes al registro privado
  3. Usa –ignore-scripts en tus pipelines de CI/CD para desactivar preinstall y postinstall de forma predeterminada

 

La seguridad de la cadena de suministro no es una tarea que se pueda «configurar y olvidarse», como un horno asador Ronco; requiere una vigilancia constante a medida que avanza el proyecto. Es algo de lo que hay que ocuparse desde el principio y durante mucho tiempo, a medida que el proyecto evoluciona.

Protege tus registros, verifica tus ámbitos y supervisa lo que ocurre en lo más profundo de tu árbol de dependencias. Los mayores riesgos no siempre son visibles, pero a menudo ya se encuentran en tu pila. Siguiendo con la metáfora del vampiro al que no se le invita a entrar, os dejo con esta cita de *The Lost Boys*: «Hay una cosa de vivir en Santa Carla que nunca pude soportar: todos esos malditos vampiros.» Santa Carla es el software, y los vampiros son los fallos de seguridad. Mantente un paso por delante y no les dejes entrar.

 

Resumen
Ataque de confusión de dependencias: cómo funciona y cómo prevenirlo
Nombre del artículo
Ataque de confusión de dependencias: cómo funciona y cómo prevenirlo
Descripción
Descubre cómo funcionan los ataques de confusión de dependencias, cómo aprovechan npm y las dependencias transitivas, y cómo proteger tu cadena de suministro de software.
Autor
Nombre del editor
TuxCare
Logotipo de la editorial

Únase a más de 4.500 profesionales de Linux y el código abierto.

2 veces al mes. Sin spam.

Índice
Obtenga las respuestas de seguridad de código abierto que necesita

Únase a más de 4.500 profesionales de Linux y el código abierto.

2 veces al mes. Sin spam.