Administración de dependencias

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 y los pasos que debes seguir a fin de reducir el impacto de tus dependencias y admitir compilaciones reproducibles.

Las dependencias de la aplicación son tecnologías necesarias para compilar o ejecutar una aplicación. Algunos ejemplos de dependencias de software incluyen sistemas operativos, bibliotecas de software, complementos, bases de datos o plataformas.

Las dependencias pueden incluir componentes que crees y software de terceros. El enfoque que adoptes para administrar las dependencias puede afectar la seguridad y confiabilidad de tus aplicaciones.

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

Enfoques para incluir dependencias

Existen varios métodos comunes para incluir dependencias en la aplicación:

Instala la app 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, ya que no necesitas mantener tus dependencias externas. Sin embargo, como no controlas estas dependencias externas, tu cadena de suministro de software es más propensa a los ataques de código abierto.
Almacena copias de las dependencias en tu repositorio de código fuente
Este enfoque también se conoce como vender. En lugar de instalar una dependencia externa de un repositorio público durante las compilaciones, puedes descargarla y copiarla en el árbol fuente del proyecto. Tienes más control sobre las dependencias de proveedores que usas, pero hay varias desventajas:
  • Las dependencias de proveedores aumentan el tamaño del repositorio de código fuente y generan más deserción.
  • Debes incluir las mismas dependencias en cada aplicación separada. Si tu repositorio de código fuente 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
Artifact Registry proporciona la comodidad de instalar desde un repositorio público y controlar las dependencias.
  • Puedes configurar los clientes de paquetes de lenguaje y Docker para que interactúen con repositorios privados en Artifact Registry de la misma manera que lo hacen con los repositorios públicos.
  • Puedes controlar las dependencias de tus repositorios privados y restringir el acceso a cada uno.
  • Tus dependencias están centralizadas para todas tus aplicaciones.
  • Tus repositorios están integrados de forma estrecha en los entornos de ejecución de Cloud Build y Google Cloud, como Google Kubernetes Engine y Cloud Run.
  • Puedes aprovechar la administración de metadatos, el análisis de vulnerabilidades y los flujos de trabajo de aprobación de implementaciones mediante Container Analysis y la autorización binaria.

Cuando sea posible, usa un registro privado para tus dependencias. En situaciones en las que no puedes usar un registro privado, considera usar tus dependencias para tener 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 versión específico. Lo ideal es que fijes una sola versión de una dependencia.

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

Puedes mitigar este problema mediante herramientas automatizadas de administración de dependencias que supervisan las dependencias en tus repositorios de origen para las nuevas versiones. Estas herramientas actualizan los archivos de requisitos para actualizar las dependencias según sea necesario, a menudo, la información de registro de cambios o los detalles adicionales.

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

Verificación de hash y firma

Existen 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 que se genera para un archivo que actúa como un identificador único. Puedes comparar el hash de un artefacto con el valor de hash que calcula 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 a través de un ataque de intermediarios o un compromiso del repositorio de artefactos.

El uso de la verificación de hash requiere confiar en que el hash que recibes del repositorio de artefactos en el momento de la verificación (o en el momento de la primera recuperación) no esté comprometido.

Verificación de firmas

La verificación de firmas agrega seguridad adicional al proceso de verificación. El repositorio de artefactos, los encargados de mantenimiento del software o ambos pueden firmar artefactos. Los servicios como sigstore proporcionan una forma para que los encargados de mantenimiento firmen artefactos de software y que los consumidores verifiquen esas firmas.

Bloquea archivos y dependencias compiladas

Los archivos de bloqueo se resuelven por completo, especificando exactamente qué versión de cada dependencia se debe instalar para una aplicación. Por lo general, las herramientas de instalación las producen de forma automática. Los archivos de bloqueo combinan la fijación de versiones y la firma o la verificación de 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 dependencias 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 suelen depender de código abierto de código abierto de terceros y de bibliotecas internas de código cerrado. Artifact Registry te permite compartir la lógica empresarial entre varias aplicaciones y reutilizar la misma herramienta para instalar bibliotecas internas y externas.

Sin embargo, cuando combinas 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 pueden aprovechar a 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 varios pasos:

  • Verifica la firma o los hashes de tus dependencias si los incluyes en un archivo de bloqueo
  • Separa la instalación de dependencias internas y de terceros en dos pasos distintos
  • Duplica de manera explícita las dependencias de terceros que necesitas en el repositorio privado, ya sea de forma manual o con un proxy de extracción.
  • Para el desarrollo basado en contenedores, usa fuentes de confianza para tus imágenes base. Google proporciona imágenes base administradas que puedes usar directamente y una canalización de imagen segura para generar tus propias imágenes base.

Quita las dependencias que no se usan

A medida que tus necesidades cambien y tu aplicación evolucione, puedes cambiar o dejar de usar algunas de tus dependencias. Si continúas instalando dependencias que no usas, tu aplicación aumentará el alcance de tus dependencias y aumentará el riesgo de que tu seguridad se vea comprometida por ellas.

Una vez que tu aplicación funciona de manera local, se recomienda copiar todas las dependencias que instalaste durante el proceso de desarrollo en el archivo de requisitos de tu aplicación. Luego, implementas la aplicación con todas esas dependencias. Este enfoque ayuda a garantizar que la aplicación implementada funcione, pero también es probable que se ingresen dependencias que no necesitas en la producción.

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

Análisis de vulnerabilidades

Responder con rapidez a las vulnerabilidades de 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 necesitas y te notifican cuando aparecen nuevas vulnerabilidades, a veces, incluso con las rutas de actualización sugeridas.

Las herramientas como Container Analysis pueden proporcionar una amplia variedad de análisis de vulnerabilidades para las imágenes de contenedores y artefactos de lenguajes como el análisis de paquetes de Java. Cuando se habilita, esta función identifica las vulnerabilidades de los paquetes en tus imágenes de contenedor. Las imágenes se analizan cuando se suben a Artifact Registry, y los datos se supervisan de forma continua para encontrar vulnerabilidades nuevas hasta por 30 días después de enviarlas.

También puedes usar el análisis a pedido para analizar las imágenes del contenedor de manera local. Esto te permite identificar las vulnerabilidades con anticipación para que puedas abordarlas antes de almacenarlas en Artifact Registry.