"Todo" y el fregadero de Node.js también
- *La cadena de suministro es vulnerable a todos los niveles, desde el código hasta la distribución.
- *El repositorio de Node.js se bloqueó después de que un desarrollador subiera un paquete malicioso.
A menudo es difícil diferenciar entre consecuencias intencionadas y no intencionadas. Una reciente "broma" durante las fiestas complicó la vida a los desarrolladores de Node.js, subrayando un conocido adagio: ninguna buena acción queda sin castigo.
Hay algo de narración, por lo que se recomiendan las palomitas.
En los lejanos tiempos de 2016...
...un desarrollador de Node.js creó un paquete llamado "leftpad", de apenas 11 líneas de código. Rellenar cadenas es una tarea habitual para los desarrolladores, que normalmente prefieren no reinventar la rueda (el principio de programación Don't Repeat Yourself - DRY).
Naturalmente, muchos optaron por importar "leftpad" a sus proyectos. Pero esta confianza introducía una vulnerabilidad: la exposición a cualquier problema con "leftpad", ya fueran errores, lagunas de seguridad o caprichos de su creador. En un giro dramático, el creador de "leftpad", invocando el derecho a retirar su obra, hizo precisamente eso. Esta acción colapsó un número significativo de sitios web importantes, poniendo de relieve la fragilidad de tales dependencias. Para ser justos, el autor dio algunas razones para hacerlo, y el hecho de que usted esté de acuerdo o no con la comercialización de código fuente abierto por parte de empresas cambiará su opinión al respecto (puede leer más al respecto aquísi le apetece).
En última instancia, la eliminación de "leftpad" causó graves trastornos en numerosos paquetes y sitios web de terceros. Para evitar que se repitiera, el registro npm implementó una política: un paquete no puede ser despublicado si es una dependencia de otro paquete. Esta norma pareció eficaz durante algún tiempo.
Todo eso está bien, pero ¿qué ha pasado recientemente?
Como obviamente no podemos tener cosas bonitas, la gente se puso a buscar formas potenciales de abusar del cambio introducido por el registro. Y, invirtiendo la lógica, si yo no puedo eliminar un paquete del que depende otro, quizá otro no pueda eliminar el suyo si mi código depende de su paquete... y, por supuesto, funcionó.
Así que, en lo que el desarrollador describió más tarde como una "prueba" y una "broma", se añadió al registro un paquete llamado "everything". No hacía otra cosa que incluir, como dependencias, un grupo de otros paquetes llamados "everything chunk 1", "everything chunk 2", etc. Estos, a su vez, incluían cada uno de los paquetes en el registro. Esto hacía imposible despublicar cualquier paquete, por cualquiera, ya que siempre habría una dependencia de él.
Pero mejora. O peor, dependiendo de la cantidad de palomitas que te sobren.
La declaración de dependencia en un paquete npm permite a los desarrolladores "depender" de una versión específica. Normalmente, se trata de una versión probada que hace lo que dice hacer, correctamente, y el desarrollador que incluye ese paquete ha realizado pruebas exhaustivas para asegurarse de que el comportamiento es el necesario para su paquete o aplicación.
Pero no está restringido a especificar una única versión. Al establecer "*" como el valor de la versión, efectivamente le dices a npm que *cualquier* versión de ese paquete puede ser usada como dependencia.
Bueno, "todo" incluía cada una de las dependencias... y cada una de las versiones de esas dependencias, ya que utilizaba "*" en la declaración.
Así, los demás desarrolladores no sólo no podían eliminar sus propios paquetes (por ejemplo, si tenían un error), sino tampoco cualquier versión antigua.
"Todo" estaba bien cerrado.
¿Se resolvió?
Sí, afortunadamente. El creador de "everything" colaboró con el equipo de soporte de npm para rectificar el problema, siendo necesaria su intervención marcando los repositorios de "everything chunk" como privados, rompiendo así la cadena de dependencias (más sobre esto, incluyendo una captura de pantalla del desarrollador disculpándose, puede verse aquí).
Este incidente ha estado a punto de afectar a la comunidad de ciberseguridad e informática. Plantea una posibilidad escalofriante: en lugar de que un desarrollador creara este caos como una broma (o una "prueba"), ¿y si hubiera sido un actor de amenazas serio? ¿Y si un paquete del estilo "todo" fuera sólo la punta del iceberg, utilizado para prohibir la eliminación de otro paquete con una vulnerabilidad de seguridad de nivel log4j en su código?
El ecosistema npm y, de hecho, cualquier entorno similar centrado en la dependencia de paquetes, aún se enfrenta a importantes retos a la hora de abordar estas vulnerabilidades. Es posible que no haya una solución perfecta, dada la creatividad con la que la gente explota los fallos.
Tal vez, en esta batalla en curso, la IA pueda ofrecer una ayuda muy necesaria.
* Para más ejemplos de paquetes aparentemente triviales pero impactantes, considere el "Un paquete rpm "is-windows" tiene 2.5 millones de dependientes, ¿por qué?" en Reddit. A un único desarrollador se le atribuyen más de 1.400 paquetes, la mayoría de ellos de una sola línea, lo que demuestra la sorprendente influencia del código mínimo.