Administración de dependencias

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

En este documento, se describen las dependencias de la aplicación y las prácticas recomendadas para administrarlas, incluida la supervisión de vulnerabilidades, la verificación de artefactos, la reducción de tu huella de dependencia y la compatibilidad con compilaciones reproducibles.

Una dependencia de software es una pieza de software que tu aplicación requiere para funcionar, como una biblioteca de software o un complemento. La resolución de dependencias puede ocurrir cuando compilas, compilas, ejecutas, descargas o instalas el software.

Las dependencias pueden incluir componentes que creas, software de terceros propietario y software de código abierto. El enfoque que adoptes para administrar dependencias puede afectar la seguridad y confiabilidad de tus aplicaciones.

Los detalles específicos para implementar las prácticas recomendadas pueden variar según el formato del artefacto y las herramientas que uses, pero los principios generales se aplican de todas formas.

Dependencias directas y transitivas

Tus aplicaciones pueden incluir dependencias directas y transitivas:

Dependencias directas
Componentes de software a los que una aplicación hace referencia directamente
Dependencias transitivas
Componentes de software que las dependencias directas de una aplicación requieren de forma funcional. Cada dependencia puede tener sus propias dependencias indirectas y directas, lo que crea un árbol recursivo de dependencias transitivas que afectan a la aplicación.

Los diferentes lenguajes de programación ofrecen diferentes niveles de visibilidad de las dependencias y sus relaciones. Además, algunos lenguajes usan administradores de paquetes para resolver el árbol de dependencias cuando instalan o implementan un paquete.

En el ecosistema de Node.js, los administradores de paquetes npm y larn usan archivos de bloqueo a fin de identificar las versiones de dependencias para compilar un módulo y las versiones de dependencias que un administrador de paquetes descarga para una instalación específica del módulo. En otros ecosistemas de lenguaje, como Java, la compatibilidad con la introspección de dependencias es más limitada. Además, los sistemas de compilación deben usar administradores de dependencias específicos para administrar las dependencias de forma sistemática.

Como ejemplo, considera el módulo npm glob versión 8.0.2. Debes declarar dependencias directas para los módulos de npm en el archivo package.json. En el archivo package.json para glob, la sección dependencies enumera las dependencias directas del paquete publicado. En la sección devDepdencies, se enumeran las dependencias para el desarrollo y las pruebas locales de los encargados de mantener y los colaboradores de glob

  • En el sitio web de npm, la página glob enumera las dependencias directas y las de desarrollo, pero no indica si estos módulos también tienen sus propias dependencias.

  • Puedes encontrar información de dependencia adicional sobre glob en el sitio de estadísticas de código abierto. La lista de dependencias para glob incluye dependencias directas y dependencias indirectas (transitivas).

    Una dependencia transitiva puede tener varias capas en el árbol de dependencias. Por ejemplo:

    1. glob 8.0.2 tiene una dependencia directa en minimatch 5.0.1.
    2. minimatch 5.0.1 tiene una dependencia directa brace-expression 2.0.1.
    3. brace-expression 2.0.1 tiene una dependencia directa en balanced-match 1.0.2.

Sin visibilidad en las dependencias indirectas, es muy difícil identificar y responder a las vulnerabilidades y otros problemas que se originan de un componente al que tu código no hace referencia directamente.

Cuando instalas el paquete glob, npm resuelve todo el árbol de dependencias y guarda la lista de versiones descargadas específicas en el archivo package.lock.json para que tengas un registro de todas las dependencias. Las instalaciones posteriores en el mismo entorno recuperarán las mismas versiones.

Herramientas para estadísticas de dependencias

Puedes usar las siguientes herramientas para comprender tus dependencias de código abierto y evaluar la posición de seguridad de tus proyectos. Estas herramientas proporcionan información en todos los formatos de paquetes.

Software Delivery Shield
Una solución de seguridad de cadena de suministro de software completamente administrada en Google Cloud que te permite ver estadísticas de seguridad de tus artefactos en Cloud Build, Cloud Run y GKE, incluidas las vulnerabilidades, la información de dependencia (vista previa privada) y la procedencia de la compilación. Software Delivery Shield también proporciona otros servicios y funciones para mejorar tu posición de seguridad en todo el ciclo de vida del desarrollo de software.
Herramientas de código abierto

Hay varias herramientas de código abierto disponibles, incluidas las siguientes:

  • Estadísticas de código abierto: Un sitio web que proporciona información sobre dependencias indirectas y directas conocidas, e información de licencias para software de código abierto. El proyecto de código abierto de estadísticas también hace que estos datos estén disponibles como un conjunto de datos de Google Cloud. Puedes usar BigQuery para explorar y analizar los datos.

  • Base de datos de vulnerabilidades de código abierto: Una base de datos de vulnerabilidades de búsqueda que agrega vulnerabilidades de otras bases de datos en una ubicación.

  • Cuadros de evaluación: Es una herramienta automatizada que puedes usar para identificar prácticas peligrosas en la cadena de suministro de software en tus proyectos de GitHub. Realiza verificaciones en los repositorios y le da a cada verificación una puntuación de 0 a 10. Luego, puedes usar las puntuaciones para evaluar la posición de seguridad de tu proyecto.

  • Allstar: Una app de GitHub que supervisa de forma continua organizaciones o repositorios de GitHub para asegurarse de que cumplen con las políticas configuradas Por ejemplo, puedes aplicar una política a tu organización de GitHub que verifique si hay colaboradores fuera de la organización que tengan acceso de administrador o de envío.

Enfoques para incluir dependencias

Existen varios métodos comunes para incluir dependencias con tu aplicación:

Instalar directamente desde fuentes públicas
Instala dependencias de código abierto directamente desde repositorios públicos, como Docker Hub, npm, PyPI o Maven Central. Este enfoque es conveniente porque no necesitas mantener tus dependencias externas. Sin embargo, como no controlas estas dependencias externas, la cadena de suministro de software es más propensa a los ataques a la cadena de suministro de código abierto.
Almacena copias de dependencias en tu repositorio de código fuente
Este enfoque también se conoce como proveedor. En lugar de instalar una dependencia externa de un repositorio público durante tus compilaciones, descárgala y cópiala en el árbol de fuentes de tu proyecto. Tienes más control sobre las dependencias de proveedores que usas, pero hay varias desventajas:
  • Las dependencias encamadas aumentan el tamaño de tu repositorio de código fuente y presentan más deserción.
  • Debes proporcionar las mismas dependencias a cada aplicación por separado. Si tu repositorio de origen o proceso de compilación no admite módulos de origen reutilizables, es posible que debas mantener varias copias de tus dependencias.
  • Actualizar dependencias de proveedores puede ser más difícil.
Almacena dependencias en un registro privado
Un registro privado, como Artifact Registry, proporciona la comodidad de instalación desde un repositorio público y control sobre tus dependencias. Con Artifact Registry, puedes hacer lo siguiente:
  • Centraliza tus artefactos y dependencias de compilación para todas tus aplicaciones.
  • Configura tus clientes de paquetes de lenguajes y Docker para interactuar con repositorios privados en Artifact Registry de la misma manera que lo hacen con repositorios públicos.
  • Obtén un mayor control sobre tus dependencias en repositorios privados:
  • Restringe el acceso a cada repositorio con la administración de identidades y accesos.
  • Usa repositorios remotos para almacenar en caché las dependencias de fuentes públicas ascendentes y analizarlas en busca de vulnerabilidades (vista previa privada).
  • Usa repositorios virtuales para agrupar repositorios privados y remotos detrás de un solo extremo. Establece una prioridad en cada repositorio para controlar el orden de búsqueda cuando descargues o instales un artefacto (vista previa privada).
  • Usa Artifact Registry con facilidad mediante otros servicios de Google Cloud en Software Delivery Shield, incluidos Cloud Build, Cloud Run y Google Kubernetes Engine. Usa el análisis automático de vulnerabilidades durante el ciclo de vida del desarrollo de software, genera el origen de las compilaciones, controla las implementaciones y obtén estadísticas sobre tu posición de seguridad.

Cuando sea posible, usa un registro privado para tus dependencias. En situaciones en las que no puedas usar un registro privado, considera proveedores tus dependencias para que tengas control sobre el contenido en tu cadena de suministro de software.

Versiones fijadas

La fijación de versiones implica restringir una dependencia de la aplicación a una versión o un rango de versiones específicos. Lo ideal es fijar una sola versión de una dependencia.

Fijar la versión de una dependencia ayuda a garantizar que las compilaciones de tu aplicación sean reproducibles. Sin embargo, también significa que tus compilaciones no incluyen actualizaciones de la dependencia, como correcciones de seguridad, errores o mejoras.

Puedes mitigar este problema con las herramientas automatizadas de administración de dependencias que supervisan las dependencias en los repositorios de origen para detectar versiones nuevas. Estas herramientas actualizan tus archivos de requisitos para actualizar las dependencias según sea necesario, por lo que suelen incluir información del registro de cambios o detalles adicionales.

La fijación de versiones solo se aplica a dependencias directas, no transitivas. Por ejemplo, si fijas la versión del paquete my-library, el PIN restringe la versión de my-library, pero no las versiones de software que my-library tiene una dependencia. Puedes restringir el árbol de dependencias de un paquete en algunos lenguajes con un archivo de bloqueo.

Verificación de firma y hash

Hay varios métodos que puedes usar para verificar la autenticidad de un artefacto que usas como dependencia.

Verificación de hash

Un hash es un valor generado para un archivo que actúa como identificador único. Puedes comparar el hash de un artefacto con el valor de hash calculado por el proveedor del artefacto para confirmar la integridad del archivo. La verificación de hash te ayuda a identificar el reemplazo, la manipulación o la corrupción de dependencias mediante un ataque de intermediario o una vulneración del repositorio de artefactos.

El uso de la verificación de hash requiere la confianza de que el hash que recibes del repositorio de artefactos no se vea comprometido.

Verificación de firmas

La verificación de firma agrega seguridad adicional al proceso de verificación. El repositorio de artefactos, los mantenedores del software o ambos pueden firmar artefactos.

Los servicios como sigstore proporcionan una forma para que los mantenedores firmen artefactos de software y para que los consumidores verifiquen esas firmas.

La autorización binaria puede verificar que las imágenes de contenedor implementadas en los entornos de ejecución de Google Cloud estén firmadas con certificaciones para diversos criterios.

Bloquea archivos y dependencias compiladas

Los archivos de bloqueo son archivos de requisitos completamente resueltos que especifican exactamente qué versión de cada dependencia debe instalarse para una aplicación. Por lo general, los archivos de bloqueo se producen automáticamente mediante herramientas de instalación y combinan la fijación de versiones y la verificación de firma o hash con un árbol de dependencias completo para tu aplicación.

Las herramientas de instalación crean árboles de dependencias mediante la resolución completa de todas las dependencias transitivas descendentes de tus dependencias de nivel superior y, luego, incluyen el árbol de dependencia en tu archivo de bloqueo. Como resultado, solo se pueden instalar estas dependencias, lo que hace que las compilaciones sean más reproducibles y coherentes.

Combina dependencias privadas y públicas

Las aplicaciones modernas nativas de la nube a menudo dependen de código abierto, código de terceros y bibliotecas internas de código abierto. Artifact Registry te permite compartir la lógica empresarial entre varias aplicaciones y volver a usar la misma herramienta para instalar bibliotecas internas y externas.

Sin embargo, cuando se combinan dependencias privadas y públicas, la cadena de suministro de software es más vulnerable a un ataque de confusión de dependencia. Si publicas proyectos con el mismo nombre que tu proyecto interno en repositorios de código abierto, los atacantes podrían aprovechar los instaladores mal configurados para instalar su código malicioso en lugar de tu dependencia interna.

Para evitar un ataque de confusión de dependencias, puedes seguir estos pasos:

  • Para verificar la firma o los hash de tus dependencias, inclúyelos en un archivo de bloqueo.
  • Separa la instalación de dependencias de terceros y dependencias internas en dos pasos distintos.
  • Duplica de forma explícita las dependencias de terceros que necesitas en tu repositorio privado, ya sea de forma manual o con un proxy de extracción. Los repositorios remotos de Artifact Registry son proxies de extracción para los repositorios públicos ascendentes.
  • Usa repositorios virtuales para consolidar los repositorios remotos remotos y los repositorios de Artifact Registry estándar detrás de un solo extremo. Puedes configurar prioridades para los repositorios ascendentes, de modo que tus versiones de artefactos privados siempre tengan prioridad sobre los artefactos públicos con el mismo nombre.
  • Usa fuentes confiables para imágenes base y paquetes públicos.

Quita dependencias sin usar

A medida que tus necesidades cambian y tu aplicación evoluciona, puedes cambiar o dejar de usar algunas de tus dependencias. Si continúas instalando dependencias sin usar con tu aplicación, aumentará el alcance de tu dependencia y aumentará el riesgo de que tu vulnerabilidad se vea comprometida.

Una vez que tu aplicación funcione de forma local, una práctica común es copiar cada dependencia que hayas instalado durante el proceso de desarrollo en el archivo de requisitos de tu aplicación. Luego, implementarás la aplicación con todas esas dependencias. Este enfoque ayuda a garantizar que la aplicación implementada funcione, pero también es probable que introduzca dependencias que no necesitas en producción.

Ten cuidado cuando agregues dependencias nuevas a tu aplicación. Cada uno tiene el potencial de introducir más código sobre el que no tienes control total. Como parte de tu canalización de prueba y análisis con lint normal, integra herramientas que auditen tus archivos de requisitos para determinar si realmente usas o importas tus dependencias.

Algunos lenguajes tienen herramientas para ayudarte a administrar tus dependencias. Por ejemplo, puedes usar el complemento de dependencias de Maven para analizar y administrar dependencias de Java.

Análisis de vulnerabilidades

Responder con rapidez a las vulnerabilidades en tus dependencias te ayuda a proteger tu cadena de suministro de software.

El análisis de vulnerabilidades te permite evaluar de forma automática y coherente si tus dependencias presentan vulnerabilidades en la aplicación. Las herramientas de análisis de vulnerabilidades consumen archivos de bloqueo para determinar con exactitud de qué artefactos dependes y te notifican cuando aparecen nuevas vulnerabilidades, a veces incluso con rutas de actualización sugeridas.

Por ejemplo, Container Analysis identifica las vulnerabilidades de los paquetes de SO en las imágenes de contenedores. Puede analizar imágenes cuando se suben a Artifact Registry y las supervisa de forma continua en busca de vulnerabilidades nuevas hasta 30 días después de enviarlas.

También puedes usar el análisis a pedido para analizar de forma local las imágenes de contenedor en busca de vulnerabilidades de OS, Go y Java. Esto te permite identificar las vulnerabilidades de forma temprana para que puedas resolverlas antes de almacenarlas en Artifact Registry.

¿Qué sigue?