Durante d茅cadas, la reutilizaci贸n del software se ha discutido con m谩s frecuencia de lo que realmente fue. Hoy la situaci贸n es la opuesta: los desarrolladores reutilizan los programas de otras personas todos los d铆as en forma de dependencias de software, y el problema en s铆 sigue siendo casi inexplorado.
Mi propia experiencia incluye una d茅cada de trabajo con
el repositorio interno de Google , donde las dependencias se establecen como un concepto prioritario, as铆 como el desarrollo de
un sistema de dependencia para el lenguaje de programaci贸n Go .
Las dependencias conllevan serios riesgos que a menudo se pasan por alto. La transici贸n a la reutilizaci贸n simple de las piezas de software m谩s peque帽as se ha producido tan r谩pidamente que a煤n no hemos desarrollado las mejores pr谩cticas para la selecci贸n y el uso efectivos de las dependencias. Incluso para tomar decisiones cuando son apropiadas y cuando no. El prop贸sito de este art铆culo es evaluar los riesgos y estimular la b煤squeda de soluciones en esta 谩rea.
驴Qu茅 es la adicci贸n?
En el desarrollo moderno, la
dependencia es un c贸digo adicional que se llama desde un programa. Agregar una dependencia evita la repetici贸n del trabajo ya realizado: dise帽o, escritura, prueba, depuraci贸n y soporte de una unidad de c贸digo espec铆fica. Llamamos a esta unidad de c贸digo un
paquete , aunque en algunos sistemas se usan otros t茅rminos, como una biblioteca o m贸dulo, en lugar de un paquete.
Aceptar dependencias externas es una pr谩ctica antigua: la mayor铆a de los programadores descargaron e instalaron la biblioteca necesaria, ya sea PCRE o zlib de C, Boost o Qt de C ++, JodaTime o Junit de Java. Estos paquetes tienen c贸digo depurado de alta calidad que requiere una experiencia considerable para crear. Si un programa necesita la funcionalidad de dicho paquete, es mucho m谩s f谩cil descargarlo, instalarlo y actualizarlo manualmente que desarrollar esta funcionalidad desde cero. Pero los grandes costos iniciales significan que la reutilizaci贸n manual es costosa: los paquetes peque帽os son m谩s f谩ciles de escribir usted mismo.
Un administrador de dependencias (a veces llamado administrador de paquetes) automatiza la descarga e instalaci贸n de paquetes de dependencias. Debido a que los administradores de dependencias facilitan la descarga e instalaci贸n de paquetes individuales, reducir los costos fijos hace que los paquetes peque帽os sean econ贸micos de publicar y reutilizar.
Por ejemplo, un administrador de dependencias de Node.js llamado NPM proporciona acceso a m谩s de 750,000 paquetes. Uno de ellos,
escape-string-regexp
, contiene una sola funci贸n que escapa a los operadores de expresiones regulares de los datos de entrada. Toda la implementaci贸n:
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; module.exports = function (str) { if (typeof str !== 'string') { throw new TypeError('Expected a string'); } return str.replace(matchOperatorsRe, '\\$&'); };
Antes de que aparecieran los administradores de dependencias, era imposible imaginar la publicaci贸n de una biblioteca de ocho l铆neas: demasiados gastos generales y muy pocos beneficios. Pero NPM redujo la sobrecarga a casi cero, con el resultado de que una funcionalidad casi trivial podr铆a empaquetarse y reutilizarse. A finales de enero de 2019, la dependencia
escape-string-regexp
se incorpor贸 a casi mil paquetes de NPM, sin mencionar todos los paquetes que los desarrolladores escriben para su propio uso y no publican en el dominio p煤blico.
Ahora los administradores de dependencia han aparecido para casi todos los lenguajes de programaci贸n. Maven Central (Java), Nuget (.NET), Packagist (PHP), PyPI (Python) y RubyGems (Ruby): cada uno de ellos tiene m谩s de 100,000 paquetes. El advenimiento de la reutilizaci贸n generalizada de paquetes peque帽os es uno de los mayores cambios en el desarrollo de software en las 煤ltimas dos d茅cadas. Y si no somos m谩s cuidadosos, esto conducir谩 a serios problemas.
驴Qu茅 podr铆a salir mal?
En el contexto de esta discusi贸n, un paquete es un c贸digo descargado de Internet. Agregar una dependencia conf铆a el trabajo de desarrollar este c贸digo (dise帽o, escritura, prueba, depuraci贸n y soporte) a otra persona en Internet a la que generalmente no conoce. Con este c贸digo, expone su propio programa a los efectos de todos los bloqueos y defectos de la dependencia. La ejecuci贸n de su software ahora
depende literalmente del c贸digo de un extra帽o de Internet. Para decirlo de esta manera, todo suena muy inseguro. 驴Por qu茅 alguien estar铆a de acuerdo con esto?
Estamos de acuerdo, porque es f谩cil, porque todo parece funcionar, porque todos los dem谩s tambi茅n lo hacen, y lo m谩s importante, porque parece ser una continuaci贸n natural de una pr谩ctica establecida de siglos de antig眉edad. Pero hay una diferencia importante que ignoramos.
Hace d茅cadas, la mayor铆a de los desarrolladores tambi茅n confiaban en que otros escribieran programas de los que depend铆an, como sistemas operativos y compiladores. Este software fue comprado de fuentes conocidas, a menudo con alg煤n tipo de acuerdo de soporte. Todav铆a hay espacio para
errores o destrucci贸n total . Pero al menos sab铆amos con qui茅n est谩bamos tratando y, por regla general, pod铆amos usar medidas comerciales o legales de influencia.
El fen贸meno del software de c贸digo abierto, que se distribuye gratuitamente a trav茅s de Internet, ha suplantado en gran medida la antigua pr谩ctica de comprar software. Cuando la reutilizaci贸n a煤n era dif铆cil, pocos proyectos introdujeron tales dependencias. Aunque sus licencias generalmente renuncian a cualquier "garant铆a de valor comercial e idoneidad para un prop贸sito particular", los proyectos construyeron una buena reputaci贸n. Los usuarios han tenido muy en cuenta esta reputaci贸n al tomar sus decisiones. En lugar de intervenciones comerciales y legales, vino el apoyo reputacional. Muchos paquetes comunes de esa 茅poca todav铆a gozan de una buena reputaci贸n: por ejemplo, BLAS (publicado en 1979), Netlib (1987), libjpeg (1991), LAPACK (1992), HP STL (1994) y zlib (1995).
Los administradores de lotes han reducido el modelo de reutilizaci贸n de c贸digo a una simplicidad extrema: ahora los desarrolladores pueden compartir el c贸digo con precisi贸n para funciones individuales en docenas de l铆neas. Este es un gran logro t茅cnico. Hay innumerables paquetes disponibles, y un proyecto puede incluir una gran cantidad de ellos, pero los mecanismos de confianza de c贸digo comercial, legal o de reputaci贸n son cosa del pasado. Confiamos en m谩s c贸digo, aunque hay menos razones para confiar.
El costo de hacer una mala adicci贸n puede verse como la suma de todos los posibles malos resultados en una serie del precio de cada mal resultado multiplicado por su probabilidad (riesgo).

El precio de un mal resultado depende del contexto en el que se utiliza la dependencia. En un extremo del espectro hay un proyecto de pasatiempo personal donde el precio de la mayor铆a de los malos resultados es cercano a cero: solo te diviertes, los errores no tienen un impacto real, excepto por un poco m谩s de tiempo, y la depuraci贸n de ellos puede ser incluso divertida. Por lo tanto, la probabilidad de riesgo es casi irrelevante: se multiplica por cero. En el otro extremo del espectro est谩 el software de producci贸n, que debe ser compatible durante a帽os. Aqu铆, el costo de la dependencia puede ser muy alto: los servidores pueden caerse, los datos confidenciales pueden divulgarse, los clientes pueden sufrir, las empresas incluso pueden ir a la quiebra. En producci贸n, es mucho m谩s importante evaluar y minimizar el riesgo de una falla grave.
Independientemente del precio esperado, existen algunos enfoques para evaluar y reducir los riesgos de agregar dependencias. Es probable que los administradores de paquetes se optimicen para reducir estos riesgos, mientras que hasta ahora se han centrado en reducir el costo de descarga e instalaci贸n.
Verificaci贸n de dependencia
No contratar铆a a un desarrollador del que nunca haya o铆do hablar y del que no sepa nada. Primero, aprender谩 algo sobre 茅l: verifique los enlaces, realice una entrevista, etc. Antes de depender del paquete que encontr贸 en Internet, tambi茅n es aconsejable aprender un poco sobre este paquete.
Una comprobaci贸n b谩sica puede dar una idea de la probabilidad de problemas al intentar usar este c贸digo. Si se encuentran problemas menores durante la inspecci贸n, puede tomar medidas para eliminarlos. Si el cheque revela problemas serios, puede ser mejor no usar el paquete: puede encontrar uno m谩s adecuado, o tal vez necesite desarrollarlo usted mismo. Recuerde que los paquetes de c贸digo abierto son publicados por los autores con la esperanza de que sean 煤tiles, pero sin garantizar la usabilidad o el soporte. En el caso de un fallo de producci贸n, depende de usted depurarlo. Como advirti贸 la primera
Licencia P煤blica General de GNU , 鈥渢odo el riesgo asociado con la calidad y el rendimiento del programa recae en usted. Si el programa resulta defectuoso, usted correr谩 con los costos de todo el mantenimiento, reparaci贸n o correcci贸n necesarios ".
A continuaci贸n, describimos algunas consideraciones para verificar el paquete y decidir si depender de 茅l.
Dise帽o
驴Est谩 clara la documentaci贸n del paquete? 驴La API tiene un dise帽o claro? Si los autores pueden explicar bien la API y el dise帽o a una persona, esto aumenta la probabilidad de que tambi茅n expliquen bien la implementaci贸n de la computadora en el c贸digo fuente. Escribir c贸digo para una API clara y bien dise帽ada es m谩s simple, m谩s r谩pido y probablemente menos propenso a errores. 驴Han documentado los autores lo que esperan del c贸digo del cliente para ser compatible con futuras actualizaciones? (Los ejemplos incluyen documentos de compatibilidad de
C ++ y
Go ).
Calidad del c贸digo
驴Est谩 bien escrito el c贸digo? Lee algunos fragmentos. 驴Los autores parecen ser cuidadosos, concienzudos y consistentes? 驴Se parece al c贸digo que desea depurar? Puede que tenga que hacer esto.
Desarrolle sus propias formas sistem谩ticas para verificar la calidad del c贸digo. Algo simple, como compilar en C o C ++ con advertencias importantes del compilador activadas (por ejemplo,
-Wall
), puede dar una idea de qu茅 tan en serio trabajaron los desarrolladores para evitar varios comportamientos indefinidos. Los idiomas recientes, como Go, Rust y Swift, usan la palabra clave
unsafe
para denotar c贸digo que viola el sistema de tipos; mira cu谩nto c贸digo inseguro hay. Tambi茅n son 煤tiles herramientas sem谩nticas m谩s avanzadas como
Infer o
SpotBugs . Las linters son menos 煤tiles: debe ignorar los consejos est谩ndar sobre temas como el estilo de par茅ntesis y centrarse en cuestiones sem谩nticas.
No se olvide de los m茅todos de desarrollo con los que puede no estar familiarizado. Por ejemplo, la biblioteca SQLite viene como un archivo 煤nico con 200,000 c贸digos y un encabezado de 11,000 l铆neas, como resultado de la fusi贸n de m煤ltiples archivos. El tama帽o de estos archivos levanta inmediatamente la bandera roja, pero una investigaci贸n m谩s exhaustiva conducir谩 al c贸digo fuente real para el desarrollo: un 谩rbol de archivos tradicional con m谩s de cien archivos fuente C, pruebas y scripts de soporte. Resulta que la distribuci贸n de un solo archivo se crea autom谩ticamente a partir de las fuentes originales: esto es m谩s f谩cil para los usuarios finales, especialmente aquellos que no tienen administradores de dependencias. (El c贸digo compilado tambi茅n funciona m谩s r谩pido porque el compilador ve m谩s opciones de optimizaci贸n).
Prueba
驴Hay alguna prueba en el c贸digo? 驴Puedes controlarlos? 驴Pasan? Las pruebas establecen que la funcionalidad principal del c贸digo es correcta, y se帽alan que el desarrollador est谩 tratando seriamente de mantenerlo. Por ejemplo, el 谩rbol de desarrollo SQLite contiene un conjunto de pruebas incre铆blemente detallado con m谩s de 30,000 casos de prueba individuales. Existe
documentaci贸n para desarrolladores que explica la estrategia de prueba. Por otro lado, si hay pocas o ninguna prueba, o si las pruebas fallan, esto es una se帽al de alerta seria: es probable que los cambios futuros en el paquete conduzcan a regresiones que podr铆an detectarse f谩cilmente. Si insiste en las pruebas en su c贸digo (驴verdad?), Debe proporcionar pruebas para el c贸digo que pasa a otros.
Suponiendo que existan pruebas, ejecute y pase, puede recopilar informaci贸n adicional ejecutando herramientas para analizar la cobertura del c贸digo,
detectar condiciones de carrera , verificar la asignaci贸n de memoria y detectar p茅rdidas de memoria.
Depuraci贸n
Encuentra el rastreador de errores para este paquete. 驴Hay muchos mensajes de error abiertos? 驴Cu谩nto tiempo han estado abiertos? 驴Cu谩ntos errores se corrigieron? 驴Hay alg煤n error solucionado recientemente? Si hay muchas preguntas abiertas sobre errores reales, especialmente no cerrados durante mucho tiempo, esta es una mala se帽al. Por otro lado, si los errores son raros y se solucionan r谩pidamente, eso es genial.
Apoyo
Mira la historia de los commits. 驴Cu谩nto tiempo se ha mantenido activamente el c贸digo? 驴Se apoya activamente ahora? Es probable que los paquetes que han sido respaldados activamente durante un largo per铆odo de tiempo sigan siendo compatibles. 驴Cu谩ntas personas est谩n trabajando en el paquete? Muchos paquetes son proyectos personales que los desarrolladores crean para el entretenimiento en su tiempo libre. Otros son el resultado de miles de horas de trabajo para un grupo de desarrolladores pagados. En general, los paquetes del segundo tipo generalmente corrigen los errores m谩s r谩pidamente, introducen constantemente nuevas funciones y, en general, est谩n mejor soportados.
Por otro lado, algunos c贸digos son realmente "perfectos". Por ejemplo,
escape-string-regexp
de NPM puede que nunca necesite cambiarse nuevamente.
Uso
驴Cu谩ntos paquetes dependen de este c贸digo? Los administradores de paquetes a menudo dan tales estad铆sticas, o puede ver en Internet con qu茅 frecuencia otros desarrolladores mencionan este paquete. Un mayor n煤mero de usuarios significa al menos el hecho de que para muchos el c贸digo funciona bastante bien, y los errores en 茅l se notar谩n m谩s r谩pidamente. El uso generalizado tambi茅n es una garant铆a parcial de servicio continuo: si un paquete ampliamente utilizado pierde su mantenedor, es muy probable que un usuario interesado asuma su rol.
Por ejemplo, las bibliotecas como PCRE, Boost o JUnit son incre铆blemente ampliamente utilizadas. Esto hace que sea m谩s probable, aunque ciertamente no garantiza, que los errores que pueda haber encontrado ya est茅n corregidos porque otros los encontraron antes que usted.
Seguridad
驴Funcionar谩 este paquete con entradas inseguras? Si es as铆, 驴qu茅 tan resistente es a los datos maliciosos? 驴Tiene errores que se mencionan en la
National Vulnerability Database (NVD) ?
Por ejemplo, cuando en 2006 Jeff Dean y yo comenzamos a trabajar en
Google Code Search (
grep
para bases de c贸digos p煤blicos), la popular biblioteca de expresiones regulares PCRE parec铆a ser la opci贸n obvia. Sin embargo, en una conversaci贸n con el equipo de seguridad de Google, aprendimos que PCRE tiene una larga historia de problemas, como desbordamientos de b煤fer, especialmente en el analizador. Nosotros mismos est谩bamos convencidos de esto buscando PCRE en NVD. Este descubrimiento no nos llev贸 inmediatamente a abandonar PCRE, sino que nos hizo pensar m谩s cuidadosamente sobre las pruebas y el aislamiento.
Licencia
驴El c贸digo tiene la licencia correcta? 驴Tiene siquiera una licencia? 驴La licencia es aceptable para su proyecto o empresa? Una parte sorprendente de los proyectos de GitHub no tiene una licencia clara. Su proyecto o empresa puede imponer restricciones adicionales a las licencias de dependencia. Por ejemplo, Google
proh铆be el uso de c贸digo bajo licencias como AGPL (demasiado estricto) y tipo WTFPL (demasiado vago).
Dependencias
驴Este paquete tiene sus propias dependencias? Las deficiencias en las dependencias indirectas son tan perjudiciales como las desventajas en las dependencias directas. Los administradores de paquetes pueden enumerar todas las dependencias transitivas de un paquete dado, y cada una de ellas idealmente deber铆a verificarse como se describe en esta secci贸n. Un paquete con muchas dependencias requerir谩 mucho trabajo.
Muchos desarrolladores nunca han mirado la lista completa de dependencias transitivas de su c贸digo y no saben de qu茅 dependen. Por ejemplo, en marzo de 2016, la comunidad de usuarios de NPM descubri贸 que muchos proyectos populares, incluidos Babel, Ember y React, dependen indirectamente de un peque帽o paquete llamado
left-pad
de una funci贸n de 8 l铆neas. Descubrieron esto cuando el autor de
left-pad
elimin贸 el paquete de NPM,
rompiendo inadvertidamente la mayor铆a de los ensamblados de los usuarios de Node.js. Y el
left-pad
no
left-pad
excepcional en este sentido. Por ejemplo, el 30% de los 750,000 paquetes en NPM dependen, al menos indirectamente, de
escape-string-regexp
. Adaptando la observaci贸n de Leslie Lamport de los sistemas distribuidos, el administrador de paquetes crea f谩cilmente una situaci贸n en la que una falla del paquete, cuya existencia ni siquiera conoc铆a, podr铆a inutilizar su propio c贸digo.
Pruebas de adicci贸n
El proceso de verificaci贸n debe incluir la ejecuci贸n de sus propias pruebas de paquetes. Si el paquete pas贸 la prueba y usted decide hacer que su proyecto dependa de 茅l, el siguiente paso deber铆a ser escribir nuevas pruebas enfocadas espec铆ficamente en la funcionalidad de su aplicaci贸n. Estas pruebas a menudo comienzan como programas cortos e independientes para garantizar que pueda comprender el paquete API y que haga lo que piensa (si no puede entender o no hace lo que necesita, 隆det茅ngase de inmediato!). Entonces vale la pena el esfuerzo adicional para convertir estos programas en pruebas automatizadas que se ejecutar谩n con nuevas versiones del paquete. Si encuentra un error y tiene una soluci贸n potencial, puede reiniciar f谩cilmente estas pruebas para un proyecto espec铆fico y asegurarse de que la soluci贸n no rompa nada m谩s.
Se debe prestar especial atenci贸n a las 谩reas problem谩ticas identificadas durante la revisi贸n de l铆nea de base. Para la b煤squeda de c贸digo, por experiencia previa, sab铆amos que PCRE a veces tarda mucho tiempo en ejecutar ciertas expresiones regulares. Nuestro plan inicial era crear grupos de hilos separados para expresiones regulares "simples" y "complejas". Una de las primeras pruebas fue un punto de referencia que compar贸
pcregrep
con varias otras implementaciones de
grep
. Cuando descubrimos que
pcregrep
era 70 veces m谩s lento que el
grep
m谩s r谩pido para un caso de prueba b谩sico, comenzamos a repensar nuestro plan para usar PCRE. A pesar de que finalmente abandonamos por completo PCRE, esta prueba permanece en nuestra base de c贸digo hoy.
Abstracci贸n de dependencia
La dependencia del paquete es una soluci贸n de la que puede optar en el futuro. Quiz谩s las actualizaciones llevar谩n el paquete en una nueva direcci贸n. Se pueden encontrar serios problemas de seguridad. Quiz谩s aparezca la mejor opci贸n. Por todas estas razones, vale la pena simplificar la migraci贸n del proyecto a una nueva dependencia.
Si se llama a un paquete desde muchos lugares en el c贸digo fuente del proyecto, deber谩 realizar cambios en todos estos lugares diferentes para cambiar a una nueva dependencia. Peor a煤n, si el paquete se presenta en la API de su propio proyecto, la migraci贸n a una nueva dependencia requerir谩 realizar cambios en todo el c贸digo que llama a su API, y esto puede estar fuera de su control. Para evitar tales costos, tiene sentido definir su propia interfaz junto con un envoltorio delgado que implementa esta interfaz utilizando una dependencia. Tenga en cuenta que el contenedor debe incluir solo lo que el proyecto necesita de la dependencia, y no todo lo que ofrece la dependencia. Idealmente, esto le permite reemplazar m谩s tarde otra dependencia igualmente adecuada, cambiando solo el contenedor.La migraci贸n de las pruebas para que cada proyecto use la nueva interfaz verifica la implementaci贸n de la interfaz y los contenedores, y tambi茅n simplifica la prueba de cualquier posible reemplazo para la dependencia.Para la b煤squeda de c贸digo, hemos desarrollado una clase abstracta Regexp
que define la interfaz de b煤squeda de c贸digo necesaria desde cualquier motor de expresi贸n regular. Luego escribieron una envoltura delgada alrededor de PCRE que implementa esta interfaz. Este m茅todo facilit贸 la prueba de bibliotecas alternativas y evit贸 la introducci贸n accidental del conocimiento de los componentes internos de PCRE en el resto del 谩rbol de origen. Esto, a su vez, asegura que, si es necesario, ser谩 f谩cil cambiar a otra dependencia.Aislamiento de dependencia
Tambi茅n puede ser apropiado aislar la dependencia en tiempo de ejecuci贸n para limitar el posible da帽o causado por errores en ella. Por ejemplo, Google Chrome permite a los usuarios agregar dependencias al navegador: c贸digo de extensi贸n. Cuando Chrome se lanz贸 por primera vez en 2008, introdujo una funci贸n cr铆tica (ahora est谩ndar en todos los navegadores) para aislar cada extensi贸n en un entorno limitado que se ejecuta en un proceso separado del sistema operativo. Un posible exploit en una extensi贸n mal escrita no ten铆a acceso autom谩tico a toda la memoria del navegador.y no pudo hacer llamadas inapropiadas al sistema. Para la b煤squeda de c贸digo, hasta que descartamos el PCRE por completo, el plan era aislar al menos el analizador PCRE en un entorno limitado similar. Hoy, otra opci贸n ser铆a un sandbox ligero basado en hipervisor, como gVisor . El aislamiento de dependencia reduce los riesgos asociados de ejecutar este c贸digo.Incluso con estos ejemplos y otras opciones listas para usar, aislar c贸digo sospechoso en tiempo de ejecuci贸n sigue siendo demasiado complicado y rara vez se realiza. El verdadero aislamiento requerir谩 un lenguaje completamente seguro para la memoria, sin chocar con un c贸digo sin tipo. Estos son complejos no solo en lenguajes completamente inseguros, como C y C ++, sino tambi茅n en lenguajes que proporcionan restringir operaciones inseguras, como Java cuando JNI est谩 activado, o como Go, Rust y Swift cuando habilita sus funciones inseguras. Incluso en un lenguaje seguro para la memoria como JavaScript, el c贸digo a menudo tiene acceso a mucho m谩s de lo que necesita. En noviembre de 2018, result贸 que la 煤ltima versi贸n del paquete npm event-stream
(una API de transmisi贸n funcional para eventos JavaScript) contiene c贸digo malicioso confusoagreg贸 hace dos meses y medio. El c贸digo recopil贸 billeteras bitcoin de los usuarios de la aplicaci贸n m贸vil Copay, obtuvo acceso a recursos del sistema completamente ajenos al procesamiento de flujos de eventos. Una de las muchas formas posibles de protegerse contra este tipo de problemas ser铆a un mejor aislamiento de la dependencia.Abandono de la adicci贸n.
Si la adicci贸n parece demasiado arriesgada y no puede aislarla, la mejor opci贸n puede ser abandonarla por completo, o al menos excluir las partes m谩s problem谩ticas.Por ejemplo, cuando entendemos mejor los riesgos de PCRE, nuestro plan para Google Code Search cambi贸 de "usar la biblioteca PCRE directamente" a "usar PCRE, pero poner el analizador en la caja de arena", luego en "escribir un nuevo analizador de expresiones regulares, pero guardar el motor PCRE", luego en "escriba un nuevo analizador y con茅ctelo a otro motor de c贸digo abierto m谩s eficiente". M谩s tarde, Jeff Dean y yo reescribimos el motor tambi茅n, por lo que no quedaban dependencias y descubrimos el resultado: RE2 .Si solo necesita una peque帽a parte de la dependencia, la forma m谩s f谩cil es hacer una copia de lo que necesita (por supuesto, mantener los derechos de autor relevantes y otros avisos legales). Usted asume la responsabilidad de la correcci贸n de errores, el mantenimiento, etc., pero tambi茅n est谩 completamente aislado de los riesgos mayores. Hay un dicho en la comunidad de desarrolladores de Go : "Un poco de copia es mejor que un poco de dependencia".Actualizaci贸n de dependencia
Durante mucho tiempo, la sabidur铆a generalmente aceptada en el software fue: "Si funciona, no toque nada". La actualizaci贸n conlleva el riesgo de introducir nuevos errores; sin recompensa: si no necesita una nueva funci贸n, 驴por qu茅 arriesgarse? Este enfoque ignora dos aspectos. Primero, el costo de una actualizaci贸n gradual. En el software, la complejidad de realizar cambios en el c贸digo no se escala linealmente: diez peque帽os cambios son menos trabajosos y m谩s f谩ciles que un gran cambio correspondiente. En segundo lugar, la dificultad de detectar errores ya corregidos. Especialmente en el contexto de seguridad, donde los errores conocidos se explotan activamente, todos los d铆as sin actualizar aumentan los riesgos de que los atacantes puedan aprovechar los errores en el c贸digo anterior.Por ejemplo, considere la historia de Equifax 2017, que los ejecutivos contaron en detalle en testimonios ante el Congreso. El 7 de marzo, se descubri贸 una nueva vulnerabilidad en Apache Struts y se lanz贸 una versi贸n parcheada. El 8 de marzo, Equifax recibi贸 una notificaci贸n de US-CERT sobre la necesidad de actualizar cualquier uso de Apache Struts. Equifax lanz贸 un escaneo del c贸digo fuente y la red el 9 y 15 de marzo, respectivamente; ni una sola exploraci贸n encontr贸 servidores web vulnerables abiertos en Internet. El 13 de mayo, los atacantes encontraron servidores que los expertos de Equifax no encontraron. Utilizaron la vulnerabilidad Apache Struts para piratear la red Equifax y robaron informaci贸n personal y financiera detallada sobre 148 millones de personas en los pr贸ximos dos meses. Finalmente, el 29 de julio, Equifax not贸 un hack y lo anunci贸 p煤blicamente el 4 de septiembre. A fines de septiembre, el CEO de Equifax, as铆 como el CIO y CSO, hab铆an renunciado y se hab铆a iniciado una investigaci贸n en el Congreso.La experiencia de Equifax lleva al hecho de que, aunque los administradores de paquetes conocen las versiones que usan durante la compilaci贸n, necesita otros mecanismos para rastrear esta informaci贸n durante la implementaci贸n en producci贸n. Para el lenguaje Go, estamos experimentando con la inclusi贸n autom谩tica del manifiesto de manifiesto en cada binario para que los procesos de implementaci贸n puedan escanear los binarios en busca de dependencias que requieran actualizaci贸n. Go tambi茅n hace que esta informaci贸n est茅 disponible en tiempo de ejecuci贸n, para que los servidores puedan acceder a las bases de datos de errores conocidos e informar de manera independiente al sistema de monitoreo cuando necesiten actualizarse.Una actualizaci贸n r谩pida es importante, pero actualizar significa agregar un nuevo c贸digo al proyecto, lo que deber铆a significar actualizar la evaluaci贸n de riesgos del uso de la dependencia en funci贸n de la nueva versi贸n. Como m铆nimo, desea ver las diferencias que muestran los cambios realizados desde la versi贸n actual a las versiones actualizadas, o al menos leer las notas de la versi贸n para identificar las 谩reas problem谩ticas m谩s probables en el c贸digo actualizado. Si cambia mucho c贸digo, por lo que las diferencias son dif铆ciles de entender, esta tambi茅n es informaci贸n que puede incluir al actualizar su evaluaci贸n de riesgos.Adem谩s, debe volver a ejecutar las pruebas escritas espec铆ficamente para el proyecto para asegurarse de que el paquete actualizado sea al menos tan adecuado para el proyecto como la versi贸n anterior. Tambi茅n tiene sentido volver a ejecutar sus propias pruebas de paquetes. Si el paquete tiene sus propias dependencias, es posible que la configuraci贸n del proyecto use otras versiones de estas dependencias (m谩s antiguas o m谩s nuevas) que las utilizadas por los autores del paquete. La ejecuci贸n de sus propias pruebas de paquetes le permite identificar r谩pidamente problemas espec铆ficos de la configuraci贸n.Nuevamente, las actualizaciones no tienen que ser completamente autom谩ticas. Antes de implementar versiones actualizadas , aseg煤rese de que sean apropiadas para su entorno .Si el proceso de actualizaci贸n implica volver a ejecutar las pruebas de integraci贸n y calificaci贸n ya escritas, en la mayor铆a de los casos la demora en la actualizaci贸n es m谩s riesgosa que una actualizaci贸n r谩pida.La ventana para actualizaciones cr铆ticas de seguridad es especialmente peque帽a. Despu茅s de que Equifax pirate贸, los equipos forenses de seguridad encontraron evidencia de que los atacantes (posiblemente diferentes) explotaron con 茅xito la vulnerabilidad Apache Struts en los servidores afectados el 10 de marzo, solo tres d铆as despu茅s de que se divulgara p煤blicamente. Pero solo lanzaron un equipo all铆 whoami
.Cuida tus adicciones
Incluso despu茅s de todo esto, el trabajo no est谩 terminado. Es importante continuar monitoreando las dependencias y, en algunos casos, incluso abandonarlas.Primero, aseg煤rese de seguir usando versiones espec铆ficas de paquetes. La mayor铆a de los administradores de paquetes ahora le permiten grabar f谩cil o incluso autom谩ticamente el hash criptogr谩fico del c贸digo fuente esperado para una versi贸n dada del paquete, y luego verificar este hash cuando el paquete se descarga nuevamente a otra computadora o en un entorno de prueba. Esto garantiza que la compilaci贸n utilizar谩 el mismo c贸digo fuente de dependencia que prob贸 y prob贸. Dichos controles impidieron al atacanteevent-stream
, inyecta autom谩ticamente c贸digo malicioso en la versi贸n ya lanzada 3.3.5. En cambio, el atacante tuvo que crear una nueva versi贸n 3.3.6 y esperar a que la gente se actualice (sin mirar cuidadosamente los cambios).Tambi茅n es importante monitorear la aparici贸n de nuevas dependencias indirectas: las actualizaciones pueden introducir f谩cilmente nuevos paquetes, de los cuales ahora depende el 茅xito de su proyecto. Tambi茅n merecen su atenci贸n. En el caso, el event-stream
c贸digo malicioso estaba oculto en otro paquete flatMap-stream
, que event-stream
se agreg贸 como una nueva dependencia en la nueva versi贸n .Las dependencias progresivas tambi茅n pueden afectar el tama帽o del proyecto. Durante el desarrollo de Google Sawzall- Lenguaje de procesamiento de registros JIT: en diferentes momentos, los autores descubrieron que el binario del int茅rprete principal contiene no solo JIT Sawzall, sino tambi茅n int茅rpretes PostScript, Python y JavaScript. Cada vez, el culpable result贸 ser dependencias no utilizadas declaradas por alguna biblioteca de Sawzall, combinadas con el hecho de que el sistema de compilaci贸n de Google utiliz贸 completamente la nueva dependencia. Es por eso que el compilador Go genera un error al importar un paquete no utilizado.La actualizaci贸n es el momento natural para revisar su decisi贸n de utilizar una dependencia cambiante. Tambi茅n es importante revisar peri贸dicamente cualquier adicci贸n que noest谩 cambiando 驴Parece plausible que no haya problemas de seguridad u otros errores que corregir? 驴Se abandona el proyecto? Tal vez es hora de planificar un reemplazo para esta dependencia.Tambi茅n es importante verificar dos veces el registro de seguridad de cada dependencia. Por ejemplo, Apache Struts revel贸 serias vulnerabilidades en la ejecuci贸n remota de c贸digo en 2016, 2017 y 2018. Incluso si tiene muchos servidores que lo inician y lo actualizan r谩pidamente, este historial sugiere si vale la pena usarlo.Conclusi贸n
La era de la reutilizaci贸n del software finalmente ha llegado, y no quiero minimizar los beneficios: trajo una transformaci贸n extremadamente positiva para los desarrolladores. Sin embargo, aceptamos esta transformaci贸n sin considerar completamente las posibles consecuencias. Las razones anteriores para confiar en las dependencias pierden relevancia al mismo tiempo cuando tenemos m谩s dependencias que nunca.El an谩lisis cr铆tico de dependencias espec铆ficas que describ铆 en este art铆culo representa una cantidad significativa de trabajo y sigue siendo la excepci贸n m谩s que la regla. Pero dudo que haya desarrolladores que realmente est茅n trabajando duro para hacer esto para cada posible nueva adicci贸n. Solo hice parte de este trabajo para algunas de mis propias dependencias. B谩sicamente, toda la soluci贸n se reduce a lo siguiente: "veamos qu茅 sucede". Con demasiada frecuencia, algo m谩s parece demasiado esfuerzo.Pero los ataques de Copago y Equifax son advertencias claras de problemas reales en la forma en que usamos las dependencias de software hoy en d铆a. No debemos ignorar las advertencias. Ofrezco tres recomendaciones generales.- . , , , . , .
- . , , . , , . , , , .
- . . . , . , , . , , API. .
Hay muchos buenos programas. Trabajemos juntos y descubramos c贸mo usarlo de manera segura.