Skip to main content

Cómo prevenir los paquetes maliciosos

Escrito por

27 de marzo de 2016

0 minutos de lectura

La semana pasada, CERT alertó a los usuarios sobre el riesgo de publicar o consumir un paquete malicioso de npm. Aunque este riesgo considerable no es exclusivo de npm, es más probable que ocurra en este ecosistema. Cabe señalar que, aunque sin duda se trata de un vector de ataque, en mi opinión no es una vulnerabilidad, sino una configuración predeterminada insegura.

En esta publicación explicamos el riesgo y cómo puedes protegerte. Veremos las concesiones que originaron el problema, analizaremos un escenario de explotación y compartiremos cómo mitigar el riesgo en tus propios entornos. Si solo quieres conocer las acciones que debes tomar, ve directamente a las secciones sobre mitigación.

Te recomendamos consultar 10 prácticas recomendadas de seguridad para npm, donde encontrarás tanto un archivo PDF descargable con una guía de referencia como un artículo detallado sobre cómo adoptar las pautas de seguridad de npm y aplicar prácticas seguras con npm.

El ataque

Paquetes maliciosos en 5 sencillos pasos

La raíz del problema es lo fácil que resulta publicar un paquete nuevo. Al igual que git, pip y otras herramientas, npm permite que los usuarios guarden sus credenciales en un archivo oculto (.npmrc) y luego publiquen sin que se les solicite confirmación.

Por desgracia, en este caso, hacer algo demasiado fácil puede ser contraproducente. El proceso de publicación simplificado aumenta la posibilidad de publicar por error, pero, aún más importante, les abre la puerta a los atacantes para publicar código malicioso de forma intencional. Para publicar una versión maliciosa de un paquete, el atacante solo necesita:

  1. Ejecutar código en la máquina del desarrollador.

  2. Determinar quién es el usuario de npm que inició sesión (npm whoami --silent)

  3. Descargar uno de los paquetes del usuario en una carpeta temporal

  4. Editar package.json: agregar un hook malicioso de postinstall e incrementar la versión en 0.0.1

  5. Publicar (npm publish)

De estos pasos, el único difícil es el primero. Por desgracia, la mayoría de los desarrolladores suele trabajar en entornos bastante permisivos…

¿Alguna vez instalaste algo con curl X | bash? Esa es una forma en que un atacante puede entrar: ni siquiera necesita sudo bash. O quizá instalas ocasionalmente un paquete de npm de forma local solo para probarlo. Cualquiera de sus dependencias puede tener un hook de postinstall que ejecute lo anterior.

Lo cierto es que los desarrolladores experimentamos, lo que naturalmente significa que no trabajamos en entornos impecables. Estos entornos no deberían tener permisos de publicación.

Propagación rápida, detección lenta

Una vez hecho esto, el atacante puede relajarse y dejar que su código se propague por todo el ecosistema de Node. La mayoría de los consumidores de este paquete lo cargarán mediante un rango semver que incluye implícitamente las versiones de parche (0.0.X), y recibirán esta nueva versión sin darse cuenta. También pueden hacer que el código malicioso se propague a los paquetes propiedad de sus víctimas, creando un gusano que se propaga aún más rápido.

Para empeorar las cosas, si se publicara un paquete malicioso, no hay una forma sencilla de detectar que ocurrió. npm no notifica al usuario cuando se publica una versión nueva, y tampoco hay una manera simple de ver las diferencias entre los paquetes (a diferencia de git). La mejor opción para detectar un problema así es notar el nuevo número de versión. Si mantienes activamente un proyecto de una sola persona, es probable que notes los cambios de versión; sin embargo, en proyectos inactivos o desarrollados por un equipo completo, estos cambios pueden pasar desapercibidos con facilidad.

Técnicas de mitigación

Cómo evitar publicaciones maliciosas (paquetes públicos)

Si colaboras en un paquete de npm, es tu responsabilidad protegerte. No permitas que los atacantes publiquen una versión maliciosa en tu nombre. Es sencillo: cierra la sesión.

Limítate a publicar mediante tu CI cuando una compilación de la rama master se complete correctamente; usa semantic-release o scripts personalizados. Sigue estos pasos:

  1. Invalida todos los tokens actuales. Puedes hacerlo desde la página de tokens y debes hacer lo mismo con los de todos tus colaboradores.

  2. Configura un token nuevo en tu CI. Remy escribió instrucciones detalladas para Travis, pero la mayoría de los sistemas de CI admiten variables ocultas. Ten en cuenta que, una vez configurado el token, ejecutar npm logout lo invalidará. En su lugar, elimina el archivo ~/.npmrc para cerrar la sesión.

  3. Usa git para las versiones de desarrollo. Si los usuarios necesitan acceso a una versión temporal del paquete (por ejemplo, para solucionar un problema), confírmala en una rama y pídeles que la instalen desde una confirmación de git.

Cómo evitar publicaciones maliciosas (paquetes privados)

Si usas un paquete privado, no puedes cerrar la sesión, ya que debes iniciarla para acceder a tus paquetes privados. Por suerte, npm lanzó recientemente las organizaciones y admite usuarios con acceso de solo lectura, lo que te permite darles acceso sin correr el riesgo de que publiquen paquetes.

Estos son, una vez más, los pasos que debes seguir:

  1. Crea una organización de npm. Si solo tienes un usuario, esto aumentará el costo mínimo en $7 al mes, pero vale mucho la pena.

  2. Crea un equipo con acceso de solo lectura a tus paquetes o ámbito.

  3. Agrega usuarios a ese equipo como miembros. También puedes reutilizar un usuario con acceso de solo lectura.

  4. Pide a tu equipo (y a ti) que inicien sesión como estos usuarios con acceso de solo lectura.

Estos pasos no eliminan la necesidad de tener cuidado al instalar software desconocido en tu computadora, pero al menos reducen la posibilidad de que te conviertas en un medio de distribución de código malicioso.

Cómo protegerte de los paquetes maliciosos

Como consumidor de paquetes de npm, no puedes evitar este riesgo por completo (ten en cuenta que lo mismo ocurre con otros administradores de paquetes). La única forma de mitigar el riesgo es controlar qué paquetes instalas.

Si desarrollas de forma local, considera usar npm shrinkwrap para fijar las versiones de los paquetes que usas. shrinkwrap congela las versiones que usas y no incorpora versiones nuevas a menos que se lo indiques explícitamente. Así, puedes revisar manualmente cada actualización. Es engorroso, pero te dará más seguridad.

Si formas parte de una organización, considera usar npm On-Site y permitir solo los paquetes que hayas aprobado. De nuevo, será algo engorroso, pero reducirá el riesgo de que se incorporen paquetes maliciosos. Para protegerte de verdad, asegúrate de configurar Read through cache como Off.

Ambas soluciones ralentizarán la incorporación de versiones nuevas, por lo que podrías quedar expuesto si se divulgan vulnerabilidades en versiones antiguas de estos paquetes. Si optas por ellas, te recomiendo que estés al tanto de las vulnerabilidades conocidas en tus dependencias de npm, por ejemplo, usando Snyk.

¿No puede npm simplemente solucionar esto?

¿No sería genial que npm agregara una o dos funciones y eliminara por completo este riesgo?

Por desgracia, no es posible. Este riesgo es una consecuencia natural del uso de paquetes de código abierto. Esto significa que usamos muchas pequeñas partes de código escritas por muchas personas. Aunque esto es excelente para la productividad, depender de las contribuciones de muchas personas implica que dependemos en gran medida de los autores de esos paquetes y de sus intenciones.

También es importante tener en cuenta que estas características no son exclusivas de npm. La mayoría de los demás administradores de paquetes permiten publicar sin confirmación y prácticamente todos permiten que se ejecute código personalizado de los paquetes durante la instalación. La única razón por la que este riesgo es mayor con npm es la gran cantidad de dependencias indirectas que se utilizan, lo que hace prácticamente imposible rastrearlas todas manualmente.

Dicho esto, hay algunas cosas que me encantaría que hiciera el equipo de npm para ayudar:

  1. Enviar un correo electrónico a todos los colaboradores cuando se publique una nueva versión de un paquete. Este debería ser el comportamiento predeterminado, con la opción de que los usuarios desactiven las notificaciones más adelante.

  2. Exigir credenciales para ejecutar npm publish, a menos que se use un token de automatización generado explícitamente. Esto hará que el comportamiento predeterminado (usar npm login) sea más seguro. Es probable que esto genere un cambio incompatible, así que no esperaría que ocurriera antes de npm 4.

  3. Si es posible, limitar las acciones que pueden realizar los scripts de instalación durante la instalación. Esto no eliminará el riesgo, pero lo hará mucho más difícil y, por lo tanto, menos probable.

Empieza con los desafíos de Capture the Flag

Aprende a resolver desafíos de captura la bandera con nuestro taller virtual introductorio a pedido.

Leer más

Blog

Los modelos de frontera encontraron las vulnerabilidades. Solo el atacante encontró las cadenas.

El análisis estático encontró las fallas, pero solo las pruebas de ataque en vivo demostraron cómo podían encadenarse para provocar brechas. Una comparación de Evo COS, Claude Security y Claude Code Security.

feature insights context
Blog

Los ataques autónomos ya están aquí. La defensa debe estar a su altura.

Los atacantes autónomos están reduciendo el tiempo disponible para defenderse. Descubre cómo el descubrimiento, la corrección, la validación y la prevención continuos pueden ayudar a los equipos de seguridad a seguirles el ritmo.

Blog

Por qué los agentes de programación con IA siguen generando fallas de control de acceso

Los agentes de programación con IA pueden generar lógica de autorización que compila y supera la revisión, pero expone los datos de un inquilino a otro. Descubre por qué es difícil detectar el control de acceso roto y cómo prevenirlo.