La misteriosa preocupación por la cadena de suministro del paquete npm string-width-cjs
3 de octubre de 2024
0 minutos de lecturaEsta historia comienza cuando Sébastien Lorber, encargado del mantenimiento de Docusaurus, el proyecto de documentación de código abierto basado en React, observa un cambio en el manifiesto del paquete en un pull request. Este es el cambio propuesto para el popular paquete npm cliui:

En concreto, nos llama la atención el cambio en las dependencias de npm, que usa una sintaxis poco familiar:
La mayoría de los desarrolladores esperaría ver un rango de versiones semver en el valor de un paquete o quizá una URL de Git o basada en archivos. Sin embargo, en este caso hay una sintaxis especial con el prefijo npm:. ¿Qué significa?
¿Qué son los alias de paquetes npm?
El administrador de paquetes npm admite una función de alias que permite definir reglas de resolución personalizadas para los paquetes. Así, cada vez que se hace referencia al paquete, ya sea en el código o en el archivo de bloqueo, se resuelve con el nombre y la versión especificados por el alias.
En el caso del cambio propuesto en este pull request, el paquete string-width-cjs se resolverá como el paquete string-width en la versión ^4.2.0. Esto significa que habrá una entrada del directorio node_modules para string-width-cjs, pero con el contenido de string-width@^4.2.0; el archivo de bloqueo (package-lock.json) tendrá un comportamiento similar.
Los alias de paquetes son una función del administrador de paquetes npm que puede usarse en casos como la compatibilidad entre ESM y CJS.
Dicho esto, se puede abusar de los alias de paquetes. En un artículo y una divulgación de seguridad de 2021, Nishant Jain, embajador de Snyk, demostró cómo se podía engañar al registro oficial npmjs para que proporcionara información incorrecta sobre las dependencias mediante alias de paquetes, como parte de un problema de confusión de dependencias y seguridad de la cadena de suministro.
El pull request era inofensivo y no había riesgo de un ataque a la cadena de suministro. Sin embargo, la preocupación de Sébastien por el nombre del paquete llevó al descubrimiento de un posible riesgo de seguridad.
Cómo detectar comportamientos sospechosos en archivos de bloqueo de npm relacionados con módulos maliciosos
Para examinar el pull request, Sébastien usó lockfile-lint. Esta herramienta revisa archivos de bloqueo como package-lock.json o yarn.lock para detectar indicios de manipulación y garantizar que no se hayan inyectado paquetes maliciosos en lugar del paquete npm original.
Al ejecutar la herramienta, aparecieron las siguientes advertencias:
Aviso: desarrollé lockfile-lint en 2019, después de publicar un artículo en el que revelaba el problema de seguridad de los archivos de bloqueo: por qué los archivos de bloqueo de npm pueden ser un punto ciego de seguridad para inyectar módulos maliciosos.
Alerta máxima: paquetes populares con nombres parecidos en npm
A partir de los resultados anteriores de lockfile-lint, Sébastien buscó estos nombres de paquetes en npm y, para su sorpresa, descubrió que existían en el registro público de npm:
https://www.npmjs.com/package/string-width-cjs
https://www.npmjs.com/package/strip-ansi-cjs
https://www.npmjs.com/package/wrap-ansi-cjs
Sébastien observó que estos paquetes no solo existen en npm, sino que también presentan características sospechosas. No estaban vinculados a ningún repositorio público de código fuente, no contenían código al inspeccionarlos y se habían publicado de forma anónima, sin información personal asociada.
Al revisar el paquete npm strip-ansi-cjs, vemos que no tiene un archivo README ni un repositorio de código fuente. Sin embargo, muchos paquetes legítimos y populares presentan el mismo comportamiento.
De hecho, este paquete es popular: tiene 529 dependencias (otros paquetes que dependen de él) y 7,274 descargas semanales.

Al revisar el código de strip-ansi-cjs, vemos que este paquete solo contiene un archivo: el manifiesto package.json.
Entonces, ¿por qué un paquete que no hace nada tiene tantas descargas y por qué tantos otros paquetes dependen de él?

Revisemos quién es el autor de estos paquetes npm.
Los tres paquetes pertenecen a himanshutester002 y se publicaron el año pasado con números de versión generados programáticamente. Estos son algunos aspectos interesantes:
El paquete npm
isaacs-cliuipodría ser un intento de typosquatting del fork del proyectocliuide Isaac y del paquete npm legítimo que mantiene en su espacio de nombres: @isaacs/cliui.El paquete npm
azure-sdk-for-netpodría ser un intento de llevar a cabo una campaña de confusión de dependencias para atacar paquetes privados con el mismo nombre.El paquete npm
link-deepsuplanta una funcionalidad popular relacionada con paquetes de utilidades como lodash y otros.

También puedes observar que el usuario himanshutester002 no proporciona información que permita identificarlo en su perfil de npmjs.
Ya mencionamos que el paquete npm strip-ansi-cjs tiene más de 500 paquetes que lo usan; esto podría indicar que es popular. Veamos cuáles son:

Que aparezca en la lista podría parecer convincente, pero ¿realmente lo es?
Por ejemplo, nombres como clazz-transformer, react-native-multiply o quizá gh-monoproject-cli parecen legítimos, pero ¿lo son?
Esta es la página del paquete npm react-native-multiply:

Este paquete prácticamente no tiene descargas y su autor es un usuario anónimo de npm sin información que permita identificarlo. La URL del repositorio de origen a la que redirige el paquete lleva a https://github[.]com/hasandader/react-native-multiply, un repositorio inexistente. El perfil de GitHub del usuario también parece muy sospechoso y no muestra actividad relevante.
Aunque el paquete npm parece contener código fuente, al examinarlo más de cerca vemos que es una muestra de código generada para el prototipo de una aplicación de “hola mundo”.

También cabe preguntarse: si este paquete es solo una biblioteca de multiplicación, ¿por qué necesita 776 dependencias para hacer lo siguiente?
Aunque algunos bromean con que JavaScript genera un árbol astronómico de paquetes anidados por el uso excesivo de dependencias, un proyecto con 776 dependencias directas es excesivamente grande.
Entre todas estas dependencias están los tres paquetes npm sospechosos con los que comenzó nuestra historia: string-width-cjs, strip-ansi-cjs y wrap-ansi-cjs:

Mencionamos que una de las dependencias de strip-ansi-cjs se llamaba clazz-transformer. Veamos:

Expliquemos qué sucede aquí. El paquete npm clazz-transformer usa deliberadamente un nombre incorrecto, pero en la página README aparece con el título class-transformer. Además, el repositorio de código fuente https://github[.]com/typestack/class-transformer no coincide con el nombre del paquete, lo que genera dudas sobre su legitimidad.
El archivo package.json del repositorio asociado typstack/class-transformer en GitHub es el siguiente:

El archivo package.json en GitHub no declara dependencias, pero al inspeccionar el código fuente del paquete correspondiente en npmjs, vemos las 437 dependencias incluidas en clazz-transformer. Una vez más, incluyen convenientemente los tres paquetes sospechosos *-cjs:

Otras consideraciones sobre los paquetes npm sospechosos
Antes de sacar más conclusiones, es importante mencionar algunas características de los paquetes npm que observamos:
Los paquetes de React Native parecen derivar de la herramienta de plantillas
create-react-native-library. Esta herramienta también incluye la función de ejemplo predeterminadamultiplyen el código fuente que genera para los proyectos nuevos.Los paquetes tienen estructuras de directorios, archivos y dependencias que podrían derivar de una plantilla inicial de Next.js 14, como las que se crean con
npx create-next-app@14.
Nuestros colegas de Sonatype ya habían identificado casos similares de inundación de registros de código abierto con paquetes. En esos casos, el objetivo final era que los desarrolladores se otorgaran tokens Tea, de una plataforma Web3 para monetizar el software de código abierto.
La presencia de algunos archivos tea.yaml en los paquetes mencionados refuerza la hipótesis de que uno de los objetivos de esta campaña es minar tokens Tea mediante el uso indebido de Tea.
A principios de este año, el 14 de abril de 2024, un usuario del foro de Tea publicó un comentario que refuerza la preocupación por el uso indebido de Tea:

Antes de compartir mis conclusiones, quiero agradecer sinceramente a Sébastien Lorber por su prudencia como encargado del mantenimiento y por ayudar a descubrir estos indicios de un posible ataque a la cadena de suministro de npm.
¿Qué está pasando con string-width-cjs?
A estas alturas, tengo mucha confianza en que, si sigo investigando los demás paquetes que supuestamente dependen de string-width-cjs, encontraré indicios muy dudosos de una legitimidad auténtica.
Supongo que el único propósito de todos estos paquetes dependientes y el aumento de descargas es crear una falsa apariencia de legitimidad para los tres paquetes *-cjs, de modo que, cuando llegue el momento y se presente la víctima adecuada, se instalen estos paquetes falsos y luego aparezca una nueva versión maliciosa.
Para mantenerte protegido mientras trabajas con software de código abierto, te recomiendo adoptar prácticas de seguridad y consultar específicamente estos recursos educativos:
¿Descubrimos una campaña contra la seguridad de la cadena de suministro en medio de estas malas prácticas, o todo se reduce al rastro del dinero y, por tanto, puede atribuirse al spam y al abuso de registros públicos como npm y GitHub para minar tokens Tea?
Sea cual sea el desenlace, mantente alerta.
Protege tu código mientras desarrollas
Snyk analiza tu código para detectar problemas de calidad y seguridad, y te ofrece recomendaciones para corregirlos directamente en tu IDE.
