La confianza como vulnerabilidad: ataques de cadena de suministro y dependencias
Vamos a tratar de un tema de moda y del que posiblemente hayas leído ya algo: los ataques a la cadena de suministro. No obstante, vamos a darle un toque diferente al final con una reflexión, a partir de un comentario en X de un desarrollador bastante conocido y respetado en el mundillo que ha creado un debate no menos interesante.
Pero antes de nada, por si alguna persona anda despistada...
¿Qué son los ataques a la cadena de suministro ('supply chain attacks')?
Usas software. Mucho. Todos los días. Algunas veces incluso sin darte cuenta. Lo que no nos paramos a pensar es en cómo está construido. Sí, ya sabemos que alguien (o algo, en estos tiempos) ha desarrollado esa aplicación que usas. Para crearla habitualmente no se parte desde cero, para acortar los tiempos de entrega se reutiliza código y muchísimo.
Lo normal es que el código reutilizable venga en formas de librerías, frameworks o add-ons. Y es que en cierto modo, construir software se parece a un proceso industrial en el que añades componentes a un sistema y los orquestas con un propósito determinado. Del mismo modo que un fabricante de coches no tiene por qué fabricar un alternador, le basta con seleccionar a un proveedor y adaptar el diseño a sus motores.
¿Pero qué ocurriría si el alternador tuviera un fallo por el que en determinadas circunstancias echa a arder? ¿Y si el fallo hubiera sido intencionado, con el propósito malicioso de afectar a los clientes y a la reputación del fabricante?
En el mismo sentido, los creadores de malware han encontrado un vector sobre el que inyectar malware sin necesidad de que este sea diseminado a través de campañas de phishing y similares.
Tiene su lógica, ¿para qué montar una infraestructura que lance cientos de miles de correos falsos con una tasa el 0,0001% de éxito cuando puedes infectar una dependencia de software y sentarte a esperar a que se distribuya con una actualización?
La cadena de suministro convierte la confianza en un vector de ataque.
Es como si los aqueos hubieran envenenado la fuente del agua de Troya con el parásito Giardia lambia en vez de fabricar un inmenso caballo hueco como regalo (por cierto, de ahí la popular frase Timeo danaos et dona ferentes.)
Algunos ataques reseñables
Hay una taxonomía entera de ataques, incluso MITRE en su famoso framework ATT&CK enumera algunas.
Entre otros métodos, podemos ver que o bien se infiltran en compañías que producen software e infectan el código (que es privado) o se acercan a proyectos open-source como colaboradores. Hay un componente común: el uso de ingeniería social de manera dirigida sutilmente hacia objetivos escogidos.
El ataque por excelencia (más que nada por su impacto mediático) es el que afectó en 2020 a la empresa SolarWinds por parte del grupo APT-29, al infiltrarse en la red interna de la compañía e infectó el código de su software Orion. Este software era empleado por multitud de empresas y organismos gubernamentales.
Infectar una librería es más rentable que lanzar miles de campañas de phishing.
En el mundillo open-source no podemos dejar pasar la lección que nos dejó el ataque al proyecto XZ, una utilidad de compresión ampliamente utilizada en toda clase de distribuciones Linux y su ecosistema en general. Durante dos años, una entidad bajo el nombre de 'Jia Tan' estuvo enviando código y arreglando bugs hasta que se ganó la confianza de los administradores del proyecto.
Una vez creyó que esa confianza era suficiente, a través de múltiples cuentas comenzaron a crear un escenario para precipitar la aceptación de código que incluía componentes maliciosos ofuscados entre código real, llegando a ser publicado en la versión 5.6.0 y también en la subsiguiente 5.6.1.
El código infectado estuvo presente un mes hasta que Andrew Freund, ingeniero de Microsoft advirtió y denunció su presencia. Un mes en el que todo aquel que incluyese el código de las versiones afectadas de XZ o liblzma estuvo expuesto al troyano.
El software no se construye desde cero: se ensambla a partir de dependencias.
Recordemos, dos años trabajando una confianza. Construyendo una relación con el resto de desarrolladores del proyecto y administradores. Dos años invertidos en crear una imagen específicamente para dar credibilidad y hacer bajar la guardia.
■ El problema no es este ejemplo, el problema es preguntarse si no estará ahora pasando lo mismo en los miles de proyectos que hay activos a día de hoy y en el que colaboran, la mayor parte de las veces, de forma altruista cientos de desarrolladores. Proyectos importantes que no poseen los medios o el respaldo adecuado.
Recordemos esta icónica imagen de XKCD, a modo de meme, que nos dejó la vulnerabilidad de Log4j y que denunciaba en parte la asimetría entre la importancia de algunos proyectos open-source y los medios con los que cuentan.
El elefante en la habitación: las dependencias
Si alguna vez has trabajado en algún proyecto de código y has empleado un gestor de paquetes habrás observado que a pesar de usar una sola librería el árbol de dependencias puede crecer de forma insospechada.
Por ejemplo, usando el gestor npm en un inocente proyecto de nodejs puede llegar a varios megas entre dependencias y dependencias transitivas. Esto es conocido por la comunidad.
Como caso bastante curioso está el del incidente con el paquete 'left-pad'. En 2016 el autor del paquete 'left-pad' se enzarzó en una disputa con una compañía debido al nombre de otro paquete de este desarrollador. Como no le dieron la razón, el desarrollador optó por retirar sus paquetes de los repositorios de npm.
Esto ocasionó una tremenda ola de errores en otros proyectos, despliegues e instalaciones. Al desaparecer una dependencia menor, el árbol de dependencias del proyecto no podía compilarse. Esto generó que se rompiesen miles de builds de otros muchos proyectos.
Lo más sangrante de este caso es que la funcionalidad de 'left-pad', que, repetimos, era dependencia de miles y miles de proyectos... era proporcionada por unas pocas líneas de código.
Y cuando decimos unas pocas líneas de código no nos referimos a miles o cientos de líneas, sino literalmente once líneas de código.
Fuente: Wikipedia
Como resultado, si tenemos un proyecto con miles de paquetes, entonces tenemos una casa con miles de ventanas abiertas que debemos proteger. Cada vez que agregamos una dependencia (y sus dependencias transitivas) al proyecto estamos abriendo la puerta a código que igual no conocemos del todo bien.
El problema no es usar dependencias, es no cuestionarlas.
La propuesta de Mitchell Hashimoto
Mitchell Hashimoto es conocido por ser el fundador de HashiCorp y el creador de Ghostty. Es una voz reconocida y respetada en el mundillo del desarrollo. Hace unos días, Mitchell, provocó un debate en la red social X que fue agregando más voces del mundillo y que derivó en una interesante discusión con múltiples aristas.
Hashimoto comentó en X que respecto a los ataques de cadena de suministro tenía una postura que minimizaba su exposición de forma colateral.
Su filosofía respecto a las dependencias es clara: "Haz un fork de las dependencias, solo usa lo que realmente necesites y nunca actualices salvo si se rompe algo" y respecto a actualizar: "Si estás actualizando una dependencia, te toca a ti analizar cada commit individual en el conjunto transitivo completo de dependencias. Si no ves nada convincente, ¡no actualices!"
Aunque suene a una posición extrema no deja de tener razón y maximiza uno de los dogmas de la ingeniería: si funciona, no lo toques.
Un fallo en un componente puede escalar al sistema completo.
Desde el punto de vista de la seguridad cuadra con sus principios: reducir la superficie de exposición (menos dependencias), no aceptar actualizaciones salvo que sean necesarias y revisar las contribuciones de código.
Figuras como Salvatore San Filippo (creador de Redis) y Armin Ronacher (Pallets) comentaron que tenían una visión similar respecto a las dependencias.
Desde cierto ángulo podría verse como una postura ciertamente radical. No admitir dependencias externas o minimizarlas es un lujo que solo pueden permitirse ciertos proyectos por requerimientos de seguridad, pero no todos los proyectos tienen disponible los recursos adecuados para seguir esta filosofía que necesita de revisión constante y una mano de cirujano para deshebrar dependencias.
¿Habrá un punto intermedio que nos posicione en una reducción sustancial de la exposición a ataques y nos permita acelerar la construcción de software?
El lector o lectora que haya prestado atención al artículo le habrá extrañado que no hablemos de la IA. Bien, ahora es el momento de sumarla.
Está claro que el caso de 'left-pad' nos deja una lección importante: si tu dependencia es un proyecto de 11 líneas de código elimina esa dependencia. Pero si tu proyecto necesita de una librería criptográfica como OpenSSL no te queda más remedio que usarla y vigilar de cerca las contribuciones de ese proyecto.
Aunque el problema va mucho más allá del código fuente, lo ideal es congelar las dependencias, revisar sus commits, que al final van a formar parte del proyecto en las actualizaciones, y solo subir de versión si realmente necesitamos las nuevas funcionalidades o corrigen fallos de seguridad.
Cada dependencia añadida amplía la superficie de riesgo, aunque no lo percibamos.
Aquí es donde se abre una puerta a la IA. ¿Quién se pone a revisar cientos de commits de un proyecto open-source antes de subir de versión en busca de código malicioso? Es costoso poner a una persona a hacerlo. Costoso y agotador.
Recordemos que en el caso del troyano de XZ se tardó un mes, cuando afortunadamente un desarrollador externo se dio cuenta revisando el código. Pero una IA lo puede hacer en segundos o minutos. No sustituye una auditoría humana seria, pero sí reduce enormemente el coste inicial de inspección. E incluso puedes industrializar este proceso en tu proyecto: detectas una dependencias mínima, propones a la IA reescribirla con código propio y reduces tu exposición.
Por otro lado, si necesitas actualizar un componente de terceros puedes indicar a la IA que revise los commits desde tu versión actual hasta la actualización antes de agregarla. Esto, claro, funciona con las dependencias que son proyectos open-source y por tanto pueden beneficiarse de su naturaleza de código público.
Conclusión
Los ataques de cadena de suministro han llegado para quedarse y es un vector que cada día está siendo más explotado. Las dependencias abren la puerta a estos ataques, pero es complicado deshacerse de aquellas.
Es costoso vigilar las dependencias de cerca y eliminarlas con código propio, pero tenemos un nuevo aliado en el roster que podemos poner a trabajar para nosotros y que se encargue de esas tareas.
Sin duda, la protección ante estos ataques pasa por revisar siempre que sea posible que introducimos en el proyecto. Es vital y necesario si queremos despejar dudas y reducir exposición.
Durante años automatizamos la incorporación de código de terceros. Tal vez haya llegado el momento de automatizar también su desconfianza.
Cloud Híbrida
Ciberseguridad
Data & AI
IoT y Conectividad
Business Applications
Intelligent Workplace
Consultoría y Servicios Profesionales
Pequeña y Mediana Empresa
Sanidad y Social
Industria
Retail
Turismo y Ocio
Transporte y Logística
Energía y Utilities
Banca y Finanzas
Ciudades Inteligentes
Sector Público