Gestion des dépendances

Ce document décrit les dépendances d'application 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 des builds reproductibles.

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

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

Les spécificités de l'implémentation des bonnes pratiques peuvent varier selon le format de l'artefact et les outils que vous utilisez, mais les principes généraux restent applicables.

Dépendances directes et transitives

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

Dépendances directes
Composants logiciels référencés directement par une application.
Dépendances transitives
Composants logiciels dont les dépendances directes d'une application ont besoin pour fonctionner. 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 paquets pour résoudre l'arborescence des dépendances lors de l'installation ou du déploiement d'un paquet.

Dans l'écosystème Node.js, les gestionnaires de paquets npm et yarn utilisent des fichiers de verrouillage pour identifier les versions de dépendances à compiler pour un module et les versions de dépendances qu'un gestionnaire de paquets télécharge pour une installation spécifique du module. Dans d'autres écosystèmes de langages tels que Java, l'introspection des dépendances est moins prise en charge. De plus, les systèmes de compilation doivent utiliser des gestionnaires de dépendances spécifiques pour gérer systématiquement les dépendances.

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 liste les dépendances directes du package publié. La section devDepdencies liste les dépendances pour le développement et les tests locaux par les responsables et les contributeurs de glob.

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

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

    Une dépendance transitive peut être présente sur plusieurs niveaux de l'arborescence des 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 et de répondre aux failles et autres problèmes provenant d'un composant que votre code ne référence pas directement.

Lorsque vous installez le package glob, npm résout l'ensemble 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 que vous disposiez d'un enregistrement de toutes les dépendances. Les installations ultérieures dans le même environnement récupéreront les mêmes versions.

Outils pour obtenir des 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 les formats de package.

Insights de sécurité dans la console Google Cloud
Google Cloud fournit des insights de sécurité pour vos artefacts dans Cloud Build, Cloud Run et GKE, y compris les failles, les informations sur les dépendances, la nomenclature des composants logiciels (SBOM) et la provenance de la compilation. D'autres services Google Cloud fournissent également des fonctionnalités qui améliorent votre stratégie de sécurité tout au long du cycle de vie du développement logiciel. Pour en savoir plus, consultez la présentation de la sécurité de la chaîne d'approvisionnement logicielle.
Outils Open Source

Plusieurs 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 les informations sur les licences des logiciels Open Source. Le projet Open Source Insights met également ces données à disposition en tant qu'ensemble de données Google Cloud. Vous pouvez utiliser BigQuery pour explorer et analyser les données.

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

  • Scorecards: outil automatisé que vous pouvez utiliser pour identifier les pratiques risquées de la chaîne d'approvisionnement logiciel dans vos projets GitHub. Il effectue des vérifications sur les dépôts et attribue à chaque vérification un score compris entre 0 et 10. Vous pouvez ensuite utiliser les scores pour évaluer la posture 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 une stratégie à votre organisation GitHub qui recherche les collaborateurs externes à l'organisation disposant d'un accès administrateur ou push.

Méthodes d'inclusion des dépendances

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

Installer directement à partir de sources publiques
Installez les 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 de la chaîne d'approvisionnement Open Source.
Stocker des copies des dépendances dans votre dépôt source
Cette approche est également appelée vendoring. Au lieu d'installer une dépendance externe à partir d'un dépôt public lors de 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 tierces que vous utilisez, mais cela présente plusieurs inconvénients :
  • Les dépendances fournies par le fournisseur augmentent la taille de votre dépôt de sources et entraînent une plus grande rotation.
  • Vous devez fournir les mêmes dépendances dans chaque application distincte. Si votre dépôt de sources ou votre processus de compilation n'est pas compatible avec les modules sources réutilisables, vous devrez peut-être gérer plusieurs copies de vos dépendances.
  • La mise à niveau des dépendances fournies par le fournisseur peut être plus difficile.
Stocker des dépendances dans un registre privé

Un registre privé, tel qu'Artifact Registry, permet d'installer facilement des éléments à partir d'un dépôt public et de contrôler vos dépendances. Avec Artifact Registry, vous pouvez:

  • Centralisez vos artefacts de compilation et vos dépendances pour toutes vos applications.
  • Configurez vos clients Docker et de packages 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 davantage 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 provenant de sources publiques en amont et rechercher des failles (preview privée).
    • Utilisez des dépôts virtuels pour regrouper des dépôts distants et privés derrière un seul point de terminaison. Définissez une priorité pour chaque dépôt afin de contrôler l'ordre de recherche lors du téléchargement ou de l'installation d'un artefact (version preview privée).
  • Utilisez Artifact Registry avec d'autres services Google Cloud, y compris Cloud Build, Cloud Run et Google Kubernetes Engine. Utilisez une analyse automatique des failles tout au long du cycle de développement logiciel, générez la provenance de la compilation, contrôlez les déploiements et obtenez des insights sur votre posture de sécurité.

Dans la mesure du possible, utilisez un registre privé pour vos dépendances. Si vous ne pouvez pas utiliser de registre privé, envisagez de vendre vos dépendances afin de contrôler le contenu de votre chaîne d'approvisionnement logicielle.

Épinglage

L'épinglage de version consiste à limiter une dépendance d'application à une version ou à une plage de versions spécifiques. Idéalement, vous devez épingler une seule version d'une dépendance.

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

Vous pouvez atténuer ce problème à l'aide d'outils de gestion des dépendances automatisés qui surveillent les dépendances dans vos dépôts sources pour détecter 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 de version 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'épingle limite la version de my-library, mais ne limite pas les versions du logiciel sur lequel my-library est dépendant. Dans certaines langues, vous pouvez limiter l'arborescence des dépendances d'un package à l'aide d'un fichier de verrouillage.

Validation de la signature et du hachage

Vous pouvez utiliser plusieurs méthodes pour vérifier l'authenticité d'un artefact que vous utilisez comme 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 des hachages vous aide à identifier le remplacement, la falsification ou la corruption des dépendances, par le biais d'une attaque de l'intercepteur ou d'une violation du dépôt d'artefacts.

L'utilisation de la validation de hachage nécessite de faire confiance au hachage que vous recevez du dépôt d'artefacts.

Validation de la signature

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

Des services tels que sigstore permettent aux mainteneurs de signer des artefacts logiciels et aux consommateurs de vérifier ces signatures.

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

Fichiers de verrouillage et dépendances compilées

Les fichiers de verrouillage sont des fichiers d'exigences entièrement résolus, qui spécifient exactement quelle version de chaque dépendance doit être installée pour une application. Généralement générés automatiquement par des outils d'installation, les fichiers de verrouillage combinent l'épinglage de version et la validation de signature ou de hachage avec un arbre de dépendance complet pour votre application.

Les outils d'installation créent des arborescences de dépendances en résolvant complètement toutes les dépendances transitives en aval de vos dépendances de premier niveau, puis en incluant 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érents.

Mélanger des 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 propriétaires. Artifact Registry vous permet de partager votre logique métier dans 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 logiciel est plus vulnérable aux attaques 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 par confusion de dépendances, vous pouvez prendre plusieurs 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 des dépendances internes en deux étapes distinctes.
  • Reflétez explicitement les dépendances tierces dont vous avez besoin dans votre dépôt privé, manuellement ou à l'aide d'un proxy pull-through. Les dépôts distants d'Artifact Registry sont des proxy pull-through pour les dépôts publics en amont.
  • Utilisez des dépôts virtuels pour regrouper les dépôts Artifact Registry distants et standards derrière 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 évoluent et que votre application évolue, vous pouvez modifier ou cesser d'utiliser certaines de vos dépendances. Continuer à installer des dépendances inutilisées avec votre application augmente votre empreinte de dépendance et augmente le risque d'être compromis par une faille dans ces dépendances.

Une fois que l'application fonctionne en local, il est courant de copier chaque dépendance que vous avez installée pendant le processus de développement dans le fichier de spécifications de votre application. Vous déployez ensuite l'application avec toutes ces dépendances. Cette approche permet de s'assurer 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.

Faites preuve de prudence lorsque vous ajoutez des dépendances à votre application. Chacun d'eux peut potentiellement introduire davantage de code sur lequel vous n'avez pas un contrôle total. Dans le cadre de votre pipeline de linting et de test régulier, intégrez des outils qui auditent vos fichiers d'exigences pour déterminer si vous utilisez ou importez réellement vos dépendances.

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

Analyse des failles

Réagir 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 introduisent des failles dans votre application. Les outils d'analyse des failles utilisent des fichiers de verrouillage pour déterminer exactement les artefacts sur lesquels vous dépendez et vous avertir lorsque de nouvelles failles apparaissent, parfois même avec des suggestions de chemins de mise à niveau.

Par exemple, Artifact Analysis identifie les failles dans les paquets d'OS des images de conteneurs. Il peut analyser les images lorsqu'elles sont importées dans Artifact Registry et les surveiller en permanence pour détecter de nouvelles failles pendant un maximum de 30 jours après le transfert de l'image.

Vous pouvez également utiliser l'analyse à la demande pour rechercher localement les failles OS, Go et Java dans les images de conteneur. Vous pouvez ainsi identifier les failles à un stade précoce et les corriger avant de les stocker dans Artifact Registry.

Étape suivante