Gestion des dépendances

Ce document décrit les dépendances d'applications et les bonnes pratiques à suivre pour les gérer, y compris la surveillance des failles, la vérification des artefacts, la réduction de l'empreinte des dépendances et la prise en charge de builds reproductibles.

Une dépendance logicielle est un élément logiciel dont votre application a besoin pour fonctionner, tel qu'une bibliothèque logicielle ou un plug-in. La résolution des dépendances peut se produire lorsque vous compilez du code, compilez, exécutez, téléchargez ou installez votre logiciel.

Les dépendances peuvent inclure à la fois des composants que vous créez, des logiciels tiers propriétaires et des logiciels Open Source. L'approche que vous adoptez pour gérer les dépendances peut avoir une incidence sur la sécurité et la fiabilité de vos applications.

Les spécificités de la mise en œuvre des bonnes pratiques peuvent varier en fonction du format de l'artefact et des outils que vous utilisez, mais les principes généraux s'appliquent toujours.

Dépendances directes et transitives

Vos applications peuvent inclure des dépendances directes et transitives:

Dépendances directes
Composants logiciels auxquels une application référence directement.
Dépendances transitives
Composants logiciels requis par les dépendances directes d'une application d'un point de vue fonctionnel. Chaque dépendance peut avoir ses propres dépendances directes et indirectes, créant ainsi un arbre récursif de dépendances transitives qui ont toutes un impact sur l'application.

Les différents langages de programmation offrent différents niveaux de visibilité sur les dépendances et leurs relations. De plus, certains langages utilisent des gestionnaires de packages pour résoudre l'arborescence de dépendances lors de l'installation ou du déploiement d'un package.

Dans l'écosystème Node.js, les gestionnaires de packages npm et yarn utilisent des fichiers de verrouillage pour identifier les versions de dépendance permettant de créer un module, ainsi que les versions de dépendance téléchargées par un gestionnaire de packages pour une installation spécifique du module. Dans d'autres écosystèmes de langage tels que Java, la prise en charge de l'introspection des dépendances est plus limitée. De plus, les systèmes de compilation doivent utiliser des gestionnaires de dépendances spécifiques pour les gérer systématiquement.

Prenons l'exemple du module npm glob version 8.0.2. Vous déclarez les dépendances directes des modules npm dans le fichier package.json. Dans le fichier package.json pour glob, la section dependencies répertorie les dépendances directes du package publié. La section devDepdencies répertorie les dépendances pour le développement et les tests locaux par les responsables et les contributeurs de glob.

  • Sur le site Web npm, la page glob répertorie les dépendances directes et de développement, mais n'indique pas si ces modules ont également leurs propres dépendances.

  • Vous trouverez des informations supplémentaires sur les dépendances de glob sur le site Open Source Insights. La liste de dépendances pour glob inclut à la fois les dépendances directes et indirectes (transitives).

    Une dépendance transitive peut se trouver en plusieurs couches dans l'arborescence de dépendances. Exemple :

    1. glob 8.0.2 dépend directement de minimatch 5.0.1.
    2. minimatch 5.0.1 a une dépendance directe à brace-expression 2.0.1.
    3. brace-expression 2.0.1 dépend directement de balanced-match 1.0.2.

Sans visibilité sur les dépendances indirectes, il est très difficile d'identifier les failles et d'autres problèmes provenant d'un composant auquel votre code ne fait pas référence directement, et d'y répondre.

Lorsque vous installez le package glob, npm résout l'intégralité de l'arborescence des dépendances et enregistre la liste des versions téléchargées spécifiques dans le fichier package.lock.json afin de disposer d'un enregistrement de toutes les dépendances. Les installations suivantes effectuées dans le même environnement récupèrent les mêmes versions.

Outils d'insights sur les dépendances

Vous pouvez utiliser les outils suivants pour vous aider à comprendre vos dépendances Open Source et à évaluer la stratégie de sécurité de vos projets. Ces outils fournissent des informations sur tous les formats de packages.

Software Delivery Shield
Solution de sécurité de la chaîne d'approvisionnement logicielle entièrement gérée sur Google Cloud qui vous permet de consulter des insights sur la sécurité de vos artefacts dans Cloud Build, Cloud Run et GKE, y compris les failles, les informations sur les dépendances, la nomenclature et la provenance de la compilation. Software Delivery Shield fournit également d'autres services et fonctionnalités pour améliorer votre stratégie de sécurité tout au long du cycle de développement logiciel.
Outils Open Source

Un certain nombre d'outils Open Source sont disponibles, y compris:

  • Open Source Insights: site Web qui fournit des informations sur les dépendances directes et indirectes connues, les failles connues et des informations sur les licences pour les logiciels Open Source. Le projet Open Source Insights rend également ces données disponibles sous la forme d'un ensemble de données Google Cloud. Vous pouvez utiliser BigQuery pour explorer et analyser les données.

  • Base de données de failles Open Source: base de données de failles consultable qui regroupe les failles d'autres bases de données en un seul emplacement.

  • Tableaux de données : outils automatisés que vous pouvez utiliser pour identifier les pratiques risquées concernant la chaîne d'approvisionnement logicielle dans vos projets GitHub. Elle effectue des vérifications par rapport aux dépôts et attribue à chaque vérification un score compris entre 0 et 10. Vous pouvez ensuite utiliser ces scores pour évaluer la stratégie de sécurité de votre projet.

  • Allstar: application GitHub qui surveille en permanence les organisations ou les dépôts GitHub pour vérifier qu'ils respectent les règles configurées. Par exemple, vous pouvez appliquer à votre organisation GitHub une règle qui recherche les collaborateurs externes à l'organisation disposant d'un accès administrateur ou push.

Approches de l'inclusion des dépendances

Il existe plusieurs méthodes courantes pour inclure des dépendances à votre application:

Installer directement à partir de sources publiques
Installez des dépendances Open Source directement à partir de dépôts publics, tels que Docker Hub, npm, PyPI ou Maven Central. Cette approche est pratique, car vous n'avez pas besoin de gérer vos dépendances externes. Toutefois, comme vous ne contrôlez pas ces dépendances externes, votre chaîne d'approvisionnement logicielle est plus sujette aux attaques Open Source liées à la chaîne d'approvisionnement.
Stocker des copies des dépendances dans votre dépôt source
Cette approche est également connue sous le nom de vendoring. Au lieu d'installer une dépendance externe à partir d'un dépôt public pendant vos compilations, vous la téléchargez et la copiez dans l'arborescence source de votre projet. Vous avez plus de contrôle sur les dépendances fournies par fournisseur que vous utilisez, mais il existe plusieurs inconvénients :
  • Les dépendances fournies augmentent la taille de votre dépôt source et augmentent la perte d'utilisateurs.
  • Vous devez fournir les mêmes dépendances dans chaque application distincte. Si votre dépôt source ou votre processus de compilation n'est pas compatible avec les modules sources réutilisables, vous devrez peut-être conserver plusieurs copies de vos dépendances.
  • La mise à niveau des dépendances fournies par fournisseur peut s'avérer plus difficile.
Stocker les dépendances dans un registre privé
Un registre privé, tel que Artifact Registry, permet d'installer des applications à partir d'un dépôt public et de contrôler vos dépendances. Avec Artifact Registry, vous pouvez :
  • Centralisez vos dépendances et artefacts de compilation pour toutes vos applications.
  • Configurez Docker et les clients de votre package de langage pour qu'ils interagissent avec les dépôts privés dans Artifact Registry de la même manière qu'avec les dépôts publics.
  • Contrôlez mieux vos dépendances dans les dépôts privés:
  • Limitez l'accès à chaque dépôt avec Identity and Access Management.
  • Utilisez des dépôts distants pour mettre en cache les dépendances des sources publiques en amont et les analyser afin de détecter d'éventuelles failles (version preview privée).
  • Utilisez des dépôts virtuels pour regrouper des dépôts distants et privés sur un seul point de terminaison. Définissez une priorité sur chaque dépôt pour contrôler l'ordre de recherche lors du téléchargement ou de l'installation d'un artefact (aperçu privé).
  • Utilisez facilement Artifact Registry avec d'autres services Google Cloud dans Software Delivery Shield, y compris Cloud Build, Cloud Run et Google Kubernetes Engine. Utilisez l'analyse automatique des failles tout au long du cycle de vie du développement logiciel, générez la provenance de la compilation, contrôlez les déploiements et consultez des insights sur votre stratégie de sécurité.

Si possible, utilisez un registre privé pour vos dépendances. Dans les cas où vous ne pouvez pas utiliser de registre privé, envisagez de fournir vos dépendances à des tiers afin de contrôler le contenu de votre chaîne d'approvisionnement logicielle.

Épinglage

Le blocage des versions consiste à limiter la dépendance d'une application à une version ou à une plage de versions spécifique. Idéalement, vous épinglez une seule version d'une dépendance.

Épingler la version d'une dépendance vous permet de vous assurer que les builds de votre application sont reproductibles. Toutefois, cela signifie également que vos builds n'incluent pas les mises à jour de la dépendance, y compris les correctifs de sécurité, les corrections de bugs ou les améliorations.

Vous pouvez atténuer ce problème à l'aide d'outils automatisés de gestion des dépendances qui surveillent les dépendances dans vos dépôts sources pour les nouvelles versions. Ces outils mettent à jour vos fichiers d'exigences pour mettre à niveau les dépendances si nécessaire, en incluant souvent des informations sur le journal des modifications ou des détails supplémentaires.

L'épinglage des versions ne s'applique qu'aux dépendances directes, et non aux dépendances transitives. Par exemple, si vous épinglez la version du package my-library, l'épinglage limite la version de my-library, mais ne limite pas les versions du logiciel pour lequel my-library a une dépendance. Vous pouvez restreindre l'arborescence des dépendances d'un package dans certains langages à l'aide d'un fichier de verrouillage.

Validation de la signature et du hachage

Il existe un certain nombre de méthodes que vous pouvez utiliser pour vérifier l'authenticité d'un artefact que vous utilisez en tant que dépendance.

Vérification du hachage

Un hachage est une valeur générée pour un fichier qui sert d'identifiant unique. Vous pouvez comparer le hachage d'un artefact à la valeur de hachage calculée par le fournisseur de l'artefact pour confirmer l'intégrité du fichier. La vérification du hachage vous aide à identifier le remplacement, la falsification ou la corruption des dépendances par le biais d'une attaque MITM ("man in the middle") ou d'une compromission du dépôt d'artefacts.

La validation du hachage nécessite de vous assurer que le hachage que vous recevez du dépôt d'artefacts n'est pas compromis.

Validation de la signature

La validation de la signature renforce la sécurité du processus de validation. Le dépôt d'artefacts et/ou les responsables du logiciel peuvent signer des artefacts.

Des services tels que sigstore permettent aux responsables de maintenance de signer les artefacts logiciels et aux consommateurs de valider ces signatures.

L'autorisation binaire permet de vérifier que les images de conteneurs déployées dans les environnements d'exécution Google Cloud sont signées avec des attestations pour divers critères.

Verrouiller les fichiers et les dépendances compilées

Les fichiers de verrouillage sont des fichiers d'exigences entièrement résolus, spécifiant exactement la version de chaque dépendance à installer pour une application. Généralement produits automatiquement par des outils d'installation, les fichiers de verrouillage combinent l'épinglage de version et la vérification de signature ou de hachage avec une arborescence de dépendances complète pour votre application.

Les outils d'installation créent des arborescences de dépendances en résolvant entièrement toutes les dépendances transitives en aval de vos dépendances de niveau supérieur, puis incluent l'arborescence de dépendances dans votre fichier de verrouillage. Par conséquent, seules ces dépendances peuvent être installées, ce qui rend les builds plus reproductibles et cohérentes.

Combiner les dépendances privées et publiques

Les applications cloud natives modernes dépendent souvent à la fois de code tiers Open Source et de bibliothèques internes fermées. Artifact Registry vous permet de partager votre logique métier entre plusieurs applications et de réutiliser les mêmes outils pour installer des bibliothèques externes et internes.

Toutefois, lorsque vous mélangez des dépendances privées et publiques, votre chaîne d'approvisionnement logicielle est plus vulnérable à une attaque de confusion de dépendances. En publiant des projets portant le même nom que votre projet interne dans des dépôts Open Source, les pirates informatiques peuvent exploiter des programmes d'installation mal configurés pour installer leur code malveillant au lieu de votre dépendance interne.

Pour éviter une attaque de confusion des dépendances, vous pouvez prendre un certain nombre de mesures:

  • Vérifiez la signature ou les hachages de vos dépendances en les incluant dans un fichier de verrouillage.
  • Séparez l'installation des dépendances tierces et internes en deux étapes distinctes.
  • Mettez en miroir explicitement les dépendances tierces dont vous avez besoin dans votre dépôt privé, manuellement ou à l'aide d'un proxy de transfert. Les dépôts distants Artifact Registry sont des proxys pull pour les dépôts publics en amont.
  • Utilisez des dépôts virtuels pour consolider les dépôts Artifact Registry standards et distants autour d'un seul point de terminaison. Vous pouvez configurer des priorités pour les dépôts en amont afin que les versions de vos artefacts privés soient toujours prioritaires sur les artefacts publics portant le même nom.
  • Utilisez des sources fiables pour les packages publics et les images de base.

Supprimer les dépendances inutilisées

À mesure que vos besoins et votre application évoluent, vous pouvez modifier ou cesser d'utiliser certaines de vos dépendances. En continuant à installer des dépendances inutilisées avec votre application, vous augmentez l'empreinte des dépendances et le risque d'être compromis par une faille dans ces dépendances.

Une fois que l'application fonctionne localement, il est courant de copier chaque dépendance que vous avez installée au cours du processus de développement dans le fichier d'exigences de votre application. Vous déployez ensuite l'application avec toutes ces dépendances. Cette approche permet de garantir que l'application déployée fonctionne, mais elle est également susceptible d'introduire des dépendances dont vous n'avez pas besoin en production.

Soyez prudent lorsque vous ajoutez de nouvelles dépendances à votre application. Chacun d'eux peut introduire plus de code sur lesquels vous ne contrôlez pas totalement. Dans le cadre de votre pipeline d'analyse lint et de test habituel, intégrez des outils qui auditent vos fichiers d'exigences pour déterminer si vous utilisez ou importez réellement vos dépendances.

Certains langages disposent d'outils pour vous aider à gérer vos dépendances. Par exemple, vous pouvez utiliser le plug-in de dépendance Maven pour analyser et gérer les dépendances Java.

Analyse des failles

Répondre rapidement aux failles de vos dépendances vous aide à protéger votre chaîne d'approvisionnement logicielle.

L'analyse des failles vous permet d'évaluer automatiquement et de manière cohérente si vos dépendances entraînent des failles dans votre application. Les outils d'analyse des failles utilisent des fichiers de verrouillage pour déterminer exactement de quels artefacts vous dépendez et vous avertissent lorsque de nouvelles failles apparaissent, parfois même avec les chemins de mise à niveau suggérés.

Par exemple, Artifact Analysis identifie les failles des packages OS dans les images de conteneurs. Il peut analyser les images lorsqu'elles sont importées dans Artifact Registry et les surveille en permanence afin de détecter de nouvelles failles jusqu'à 30 jours après leur transfert.

Vous pouvez également utiliser l'analyse à la demande pour analyser en local les failles OS, Go et Java dans les images de conteneurs. Cela vous permet d'identifier rapidement les failles afin de pouvoir les corriger avant de les stocker dans Artifact Registry.

Étapes suivantes