En el desarrollo de software dependemos de componentes o artefactos tanto propios como de terceros. Una gestión de dependencias flexible es esencial para el software moderno. Gestores de paquetes como NPM, Maven, pepita or NuGet se utilizan a menudo para especificar dependencias de software. Estas herramientas se diseñaron pensando en la comodidad y la facilidad de uso, no en la seguridad.
El problema
El problema es que la flexibilidad y la facilidad de uso atraen a los desarrolladores a los malos, que ven las dependencias de software como un encanto irresistible para sus negocios. El resultado: los malos actores siguieron todas las rutas de ataque posibles que se muestran aquí. Fuente: “Colección de cuchillos del traidor: una revisión de los ataques a la cadena de suministro de software de código abierto”
En esta publicación nos centraremos en el uso de declaraciones de versión abierta, en el sentido de que la versión descargada no es fija sino que debe pertenecer a un rango determinado. En el momento de la compilación, el administrador de paquetes elige y descarga/instala la versión más alta existente compatible con el rango de versiones especificado.
Ilustremos declaraciones abiertas en declaraciones de dependencias para diferentes administradores de paquetes:
-
- MNP: package.json
{
...
“dependencias”: {
...
“acepta”: “>=1.3.8”,
“lodash”: “~4.16.0”,
...
},
...
}
Se instalará la versión más grande existente no inferior a 1.3.8 para el paquete de aceptación, así como la actualización de 'parche' más grande para lodash en el rango 4.16.x.
-
- Experto: pom.xml
...
...
comunes-io
comunes-io
LIBERAR
...
...
La última versión disponible para commons-io (archivo jar) se agregará como dependencia.
-
- Pepita: configuración.py
...
configuración(
...
install_requires=['peppercorn', 'launchpadlib'],
...
)
...
Estos esquemas de versión abierta tienen un lado bueno y otro malo. Lo bueno es que las versiones más nuevas usually contienen mejoras funcionales y de calidad, correcciones de errores y parches de seguridad, que se actualizan automáticamente. Tenga en cuenta que, para la mayoría de los proyectos del mundo real, las correcciones no se respaldan en versiones menores anteriores, excepto quizás en el caso de vulnerabilidades de seguridad catastróficas. Las versiones abiertas también son buenas para usar en bibliotecas, para reducir la cantidad de versiones que deben instalarse cuando se resuelven todas las dependencias.
Pero las gamas de versiones abiertas tienen su lado malo. No sabes exactamente qué versiones se instalarán en el momento de la compilación y las compilaciones no son repetibles. Y también hay un oscuro lado con versiones abiertas. Si un mal actor logra publicar un componente malicioso en el repositorio público con una versión alta compatible con su rango abierto, su próxima compilación incluirá el componente malicioso, tal vez incluso ejecutando malware en scripts de instalación que podrían ejecutarse automáticamente. Ofuscar la carga útil del ataque es todo un arte.
Esto se conoce como el Falta de fijación de versiones problema.
Los actores maliciosos siempre intentan introducir versiones maliciosas de paquetes populares de código abierto. Pueden acceder a las claves de los repositorios de paquetes en una filtración de Secreto; a menudo emplean ingeniería social u ocultan una dependencia maliciosa anidada en un sistema aparentemente útil. pull requestIncluso algunos autores un día deciden que el mundo no es justo y muerden a sus clientes con material de protesta ¡en sus propios paquetes!
Ahora imagine que está trabajando para una organización que utiliza componentes internos además de componentes de código abierto.
Si un mal actor conoce el nombre de dichos componentes internos, puede publicar un componente con el mismo nombre en el repositorio público. Muchos administradores de paquetes obtienen primero los componentes públicos, y si la versión se elige correctamente y la versión en la dependencia declarada está abierta, ¡boom! Este problema se llama Confusión de dependencia.
Mostremos un ejemplo. Supongamos que en nuestro proyecto NPM tenemos una dependencia de un componente privado:
-
- MNP: package.json
{
"nombre": "mi-proyecto",
...
“dependencias”: {...
“mi-departamento-privado”: “>=1.0.0”,...
}
...
}
El atacante puede crear una versión principal de my-private-dep (como 99.0.0) y publicarla en el repositorio público de npm, con su propia cuenta falsa (el atacante no necesita hacer nada con mi organización). El administrador de paquetes NPM instalará la dependencia maliciosa, a menudo con resultados devastadores.
La solución
Para evitar estos problemas en nuestro proceso de creación de software, debemos seguir normas estrictas sobre cómo declarar las versiones de los componentes, que dependen de la tecnología utilizada. Lo importante es que una versión específica de un paquete, una vez publicada en un repositorio, debe ser inmutable (para evitar romper dependientes, no solo por razones de seguridad).
La idea general es fijar (alfiler), comprobando siempre que las versiones fijas de los componentes (incluidas TODAS las dependencias transitivas) estén libres de malware, y esto es posible gracias al archivos de bloqueo que ofrecen muchos administradores de paquetes. Veamos cómo funciona la fijación de versiones para diferentes administradores de paquetes. Existe un delicado equilibrio entre actualizaciones frecuentes de versiones para corregir vulnerabilidades conocidas y fijación de versión para evitar construcciones no deterministas y posibles ataques a la cadena de suministro.
-
- NPM:
Los administradores de paquetes npm o Yarn utilizan diferentes archivos de bloqueo (npm-shrinkwrap.json/paquete-lock.json o Yarn.lock, respectivamente) que enumeran versiones fijas para todas las dependencias, directas e indirectas. Los archivos de bloqueo deben estar bajo control de versiones; de lo contrario, otros desarrolladores/nodos de compilación pueden terminar con versiones diferentes. Evite la instalación de npm a menos que durante el desarrollo necesite actualizar las dependencias (por ejemplo, para instalar correcciones de seguridad). Utilice el npm ci (Instalación limpia) más determinista en general, de modo que el administrador de paquetes usará el archivo de bloqueo o terminará con un error si no hay un archivo de bloqueo o si no coincide con el paquete.json. Si se verificaron las versiones enumeradas en busca de malware, el archivo de bloqueo garantiza que no sucederá nada malo en el momento de la compilación.Para los componentes internos, se recomienda crear un Alcance del MNP administrado por la organización (como @myorg) y usar ese alcance en la dependencia (como @myorg/my-private-dep), que solo podría tener visibilidad privada. Esto bloquea confusión de dependencia ataques, ya que solo los miembros de la organización con acceso de escritura pueden publicar paquetes bajo tal alcance.
- NPM:
-
- Maven:
Maven/Gradle no tienen archivos de bloqueo (pero consulte este artículo de StackOverflow).Los rangos de versiones no se utilizan tanto con Maven/Gradle como con otros ecosistemas. Solo evita gamas de versiones y metaversiones ÚLTIMAS o DE LANZAMIENTO. También se deben comprobar las versiones indirectas. El Versiones del complemento Maven es una buena herramienta para el control de versiones.
Tenga en cuenta que Maven siempre tuvo el concepto de alcance de la organización (la parte groupId de la dependencia), y la confusión de dependencias parece no ser un problema en absoluto para ese ecosistema.
- Maven:
-
- Pip:
En Python existen diferentes herramientas para manejar archivos de bloqueo:– pipenv, que genera un archivo de bloqueo Pipfile.lock.
– poesía, que genera poesía.lock.
– congelación de pepitas, comando que genera un archivo de requisitos.txt que actúa como un archivo de bloqueo. Compruebe si todas las dependencias usan versiones fijas con el operador ==. Luego pip install -r requisitos.txt usa las dependencias fijas.
- Pip:
Recuerde que los archivos de bloqueo anteriores deben estar bajo control de versiones y que el comando de compilación elegido debe usar el archivo de bloqueo.
El repositorio de paquetes habitual utilizado con pip (PyPI) no tiene ámbitos de nomenclatura y es vulnerable a ataques de confusión de dependencias. Evitar la confusión de dependencias en el ecosistema de Python no es fácil, y algunos autores recomiendan usar un repositorio interno para actuar como proxy para las dependencias públicas obtenidas de PyPI, pero tomando primero las dependencias privadas del repositorio interno (-index-url debe apuntar al repositorio interno, no a PyPI, y –extra-index-url debe eliminarse).
Algunos ataques reales
Ataque de obtención de cookies: El actor Dustin87 agregó una dependencia indirecta en el popular paquete npm mailparser a un paquete malicioso con una puerta trasera RCE (gCOMMANDhDATAi):
JSON.stringify(req.headers).replace(/g([a-f0-9]{4})h((?:[a-f0-9]{2})+)i/gi, (o, p, v) => {})
A pesar de estar obsoleto (¡sin revisores!), Mailparser aún recibía alrededor de 64,000 descargas semanales. Este fue un caso de cuasi ataque, ya que el RCE no se ejecutó.cised.
MNP publicado esta publicación con detalles sobre el ataque getcookies.
Confusión de dependencia:
Alex Birsan descubrió en 2021 el problema de la confusión de dependencia y publicó una publicación titulada “Cómo pirateé Apple, Microsoft y docenas de otras empresas.
Recuerde que para npm el alcance de la organización como @myorg debe reservarse y los paquetes internos deben modificarse para usar el alcance.
Con pip, el registro público común PyPI no tiene ámbitos/espacios de nombres. Cada paquete privado podría tener un paquete público con el mismo nombre que el paquete interno, pero vacío, y tal vez generando un error cuando se usa, por lo que podría identificarse si se recupera accidentalmente.
Nodo-ipc:
El propietario del paquete, cuando comenzó la guerra entre Rusia y Ucrania, inyectó código malicioso para eliminar archivos aleatorios, cuando se instaló en hosts rusos y bielorrusos. El archivo ssl-geospec.js hacía dicha distinción geográfica:
Curiosamente, otros paquetes usaban versiones abiertas para la dependencia nodo-ipc, como el popular marco Vue.js, y sus mantenedores recibieron una llamamiento urgente para fijar la dependencia node-ipc en una versión segura.
Este la publicación contiene más detalles sobre este sabotaje, que va un paso más allá de otros Software de protesta infertilidad
Observaciones finales
Las versiones abiertas deben nunca ser utilizado en proyectos de software consolidados. Hacen que las compilaciones no sean reproducibles y los atacantes pueden explotarlas y lograr inyectar malware mediante ataques a los árboles de dependencia, como la confusión de dependencia antes mencionada.
Configuraciones erróneas como Versiones abiertas, falta de fijación de versiones o componentes internos sin alcance. debería ser evitado. Lo primero es detectar este tipo de problemas, tal vez incluso bloquear la compilación cuando se encuentren, y haber estandarizado un protocolo de acción.
Detección automática de fallas y configuraciones incorrectas en dependencias, reportando dependencias sospechosas que podrían ser vulnerables a ataques específicos a la cadena de suministro como confusión de dependencia, todo con herramientas de reparación prácticas, es uno de los principales objetivos de la plataforma xygeni.
| Leer más |
| Ohm M., Plate H., Sykosch A., Meier M.: “Colección de cuchillos del traidor: una revisión de los ataques a la cadena de suministro de software de código abierto”. DIMVA 2020. Lecture Notes in Computer Science, vol 12223. Springer - 2020 (fuente de la figura del árbol de ataque de dependencia). |
| @adam-npm: “Módulo malicioso reportado: getcookies“. Blog de npm (archivado) - 2 de mayo de 2018. |
| Alex Birsan: “Cómo pirateé Apple, Microsoft y docenas de otras empresas”. Medio: 9 de febrero de 2021. |
| Hacha Sharma: “GRAN sabotaje: el famoso paquete npm elimina archivos para protestar contra la guerra de Ucrania”BleepingComputer – 17 de marzo de 2022 |





