Aller au contenu

Attaques supply chain et protection des dépendances

Attaques supply chain et protection des dépendances

Section intitulée « Attaques supply chain et protection des dépendances »

Une attaque de chaîne d’approvisionnement compromet un fournisseur de code, un outil de build ou une mise à jour légitime avant de viser les organisations qui lui font confiance. Le point d’entrée est mutualisé, l’impact aussi, et la cible finale installe parfois elle-même le code hostile avec tous les privilèges demandés.

→ Lire l’explication complète de la marmotte

Compromettre un fournisseur plutôt que chaque cible

Section intitulée « Compromettre un fournisseur plutôt que chaque cible »

L’attaque directe impose de franchir les défenses de chaque cible. L’attaque supply chain compromet un composant déjà admis dans leur périmètre de confiance : bibliothèque open source, registre de paquets, serveur de mise à jour, outil de compilation, image de conteneur ou prestataire d’intégration continue. Le rendement est meilleur, ce qui intéresse beaucoup plus un attaquant qu’une démonstration de créativité sur une diapositive.

L’incident SolarWinds reste le cas d’école : du code malveillant a été inséré dans une version signée d’un produit de supervision, puis distribué par le mécanisme normal de mise à jour. La signature attestait l’origine apparente du paquet et son intégrité depuis la signature. Elle n’attestait ni l’absence de code hostile dans le build, ni l’intégrité de l’environnement qui avait produit ce build. C’est la limite à ne jamais oublier : une signature valide peut authentifier une compromission parfaitement industrialisée.

Les dépendances open source concentrent trois risques distincts. Le premier est la prise de contrôle du compte d’un mainteneur isolé, souvent par vol de session, réinitialisation abusive ou absence d’authentification multifacteur. Le second est le typosquatting : publier requets, requsets ou un nom voisin d’un paquet attendu, puis compter sur une faute de frappe dans un manifeste. Le troisième est la dépendance transitive : une bibliothèque appelée par une bibliothèque appelée par une bibliothèque, à une profondeur où plus personne ne lit le code. Le projet moderne adore appeler cela la vélocité. Le code exécuté, lui, ne partage pas cet enthousiasme.

Le compromis central est net : consommer vite des composants externes réduit le coût de développement et accélère les correctifs, mais transfère une part du contrôle vers des identités, registres et chaînes de build externes. La position défendable consiste à accepter ce transfert seulement quand chaque artefact déployable est inventorié, figé, vérifié et traçable. Refuser toute dépendance externe n’est pas une stratégie, c’est surtout une manière coûteuse de réécrire des failles déjà connues.

La cartographie des menaces doit traiter la chaîne de livraison comme une surface d’attaque autonome. Elle ne se réduit pas à l’ingénierie sociale : un vol de compte mainteneur peut être le déclencheur, mais le mécanisme de propagation repose ensuite sur une confiance technique déjà installée.

Dépendances transitives, registres et confusion de dépôt

Section intitulée « Dépendances transitives, registres et confusion de dépôt »

Un manifeste applicatif ne décrit pas toujours ce qui sera réellement exécuté. Un gestionnaire de dépendances résout des versions, télécharge des archives, applique des scripts d’installation et complète l’arbre avec des dépendances transitives. Sans fichier de verrouillage, deux builds du même commit peuvent résoudre des versions différentes. Ce n’est pas de la reproductibilité, c’est une tombola avec compilation.

Le dependency confusion exploite une configuration hybride entre registre interne et registre public. Si un paquet privé porte le même nom qu’un paquet public, et si le résolveur préfère la version publique la plus récente, un attaquant publie ce nom sur le registre public avec un numéro de version supérieur. Le build télécharge alors le mauvais composant sans compromettre le réseau ni le dépôt source. La contre-mesure n’est pas de surveiller les fautes de frappe après coup : il faut définir des espaces de noms privés réservés, configurer un proxy de registre qui interdit les résolutions publiques pour ces espaces, et journaliser chaque provenance de paquet.

Une nomenclature logicielle, ou SBOM (Software Bill of Materials), recense les composants, leurs versions, leurs empreintes et leurs relations. Elle répond à une question triviale en apparence et désagréable en incident : quel service exécute exactement telle bibliothèque vulnérable ou compromise ? Une SBOM générée uniquement au build est insuffisante si l’image réellement déployée diffère de l’artefact construit. Il faut produire la nomenclature depuis l’artefact final, l’archiver avec son empreinte et la relier au déploiement.

La SBOM ne bloque rien par elle-même. Elle ne détecte pas un code malveillant inédit, ne garantit pas que la version déclarée correspond au comportement observé, et ne remplace pas la revue des dépendances sensibles. Elle réduit le temps d’identification et permet d’appliquer une décision de blocage. Sans politique d’admission, elle devient un inventaire très élégant que personne ne consulte avant l’incident.

Les composants critiques, notamment ceux qui traitent des identités, des secrets ou des flux réseau, exigent une revue de provenance renforcée. La gestion des identités et la cryptographie appliquée dépendent fréquemment de bibliothèques dont une mise à jour mineure peut modifier les primitives, les certificats acceptés ou les chemins de confiance.

Épinglage de versions, empreintes et signatures de build

Section intitulée « Épinglage de versions, empreintes et signatures de build »

Épingler une version signifie imposer une version exacte dans le manifeste et le fichier de verrouillage. Vérifier une empreinte ajoute une contrainte cryptographique : l’archive téléchargée doit correspondre à une valeur attendue, typiquement SHA-256 (Secure Hash Algorithm à 256 bits). L’épinglage évite les mises à jour surprises. L’empreinte détecte une archive modifiée après la publication de la valeur de référence. Les deux sont nécessaires, mais aucun ne protège si le fichier de verrouillage ou l’empreinte sont eux-mêmes modifiés dans une revue bâclée.

La recommandation est ferme : interdire les plages de versions et les résolutions flottantes pour les builds de production. Les mises à jour doivent être explicites, proposées par une automatisation dédiée, évaluées par des tests, puis approuvées dans une modification distincte. Cela ralentit l’adoption automatique des correctifs. C’est le prix de la capacité à expliquer pourquoi un binaire donné a été construit avec un composant donné. Ce prix est inférieur à celui d’un rollback effectué à l’aveugle, activité traditionnellement peu appréciée des systèmes distribués.

La signature de code doit être vérifiée à chaque frontière : réception d’une dépendance, publication de l’artefact, promotion vers le registre interne et admission au déploiement. La clé de signature doit être séparée de l’environnement de build ordinaire. Un serveur de build capable à la fois de produire l’artefact et d’utiliser sans contrôle la clé qui le signe ne fournit qu’un autocollant cryptographique.

Une attestation de build doit lier l’artefact à son commit source, au fichier de verrouillage, à l’environnement de build et aux étapes exécutées. Elle apporte de la traçabilité, pas de la magie : si le compilateur est compromis ou si un script de build télécharge du code non déclaré, l’attestation documente une chaîne compromise avec une ponctualité admirable.

L’exemple suivant vérifie une archive contre une empreinte conservée dans le dépôt de livraison. La valeur de référence doit provenir d’un canal protégé et revu, pas être recalculée automatiquement depuis l’archive qui vient d’être téléchargée, sinon le contrôle applaudit le fraudeur.

Fenêtre de terminal
# Vérifier l'artefact téléchargé avant de l'ajouter au registre interne.
# artifact.sha256 contient : "<empreinte SHA-256> application-1.4.2.tar.gz"
set -eu
sha256sum -c artifact.sha256
# Refuser toute archive non vérifiée avant signature et promotion.
test "$?" -eq 0

En pipeline d’intégration continue et de livraison continue, ou CI/CD (Continuous Integration and Continuous Delivery), ce contrôle doit échouer avant toute étape ayant accès à un registre interne, un secret de déploiement ou une clé de signature. Une vérification placée après l’assemblage d’une image est trop tardive si les scripts d’installation ont déjà été exécutés.

La vérification d’empreinte ne remplace pas la vérification de signature. L’empreinte répond à « est-ce exactement le contenu attendu ? ». La signature répond à « quelle identité autorisée a produit ou approuvé ce contenu ? ». L’empreinte est excellente pour le verrouillage reproductible ; la signature gère la délégation, la rotation de clés et la révocation. Utiliser l’une pour remplacer l’autre est une économie qui finit généralement rangée dans la catégorie des économies coûteuses.

Exploitation, rotation et reprise après compromission

Section intitulée « Exploitation, rotation et reprise après compromission »

Les secrets des registres, des comptes mainteneurs et des agents de build sont des cibles de premier rang. Ils doivent être courts, limités à un dépôt et à une action, stockés hors des variables de build lisibles par les jobs non approuvés. Les agents éphémères réduisent la persistance d’une compromission, mais ne corrigent pas un secret exposé dans les journaux ou un jeton doté du droit de publier toutes les versions.

La rotation d’une clé de signature doit accepter une période de recouvrement : publier avec la nouvelle clé, distribuer la nouvelle clé publique aux vérificateurs, puis retirer l’ancienne selon une date définie. Révoquer une clé sans mécanisme de distribution fiable bloque les déploiements légitimes. Ne pas la révoquer après soupçon de fuite laisse l’attaquant signer tranquillement. La chaîne d’approvisionnement apprécie les dilemmes sans glamour.

La supervision doit corréler les publications de paquets, les changements de droits, les modifications de fichiers de verrouillage, les signatures émises et les admissions en production. Une alerte sur une dépendance nouvelle sans ticket de mise à jour, une publication hors fenêtre ou une signature émise par un agent inhabituel mérite un arrêt de promotion, pas un rapport mensuel décoratif.

Après compromission d’un composant, supprimer le paquet ne suffit pas. Il faut identifier les artefacts dérivés grâce aux SBOM et attestations, bloquer les nouvelles installations, reconstruire depuis une chaîne considérée saine, révoquer les secrets accessibles au composant et rechercher les actions postérieures à son exécution. Une restauration d’image sans rotation des identifiants laisse souvent la porte ouverte avec une image fraîchement repeinte.

Sans ces contrôles, la porte reste ouverte à tout ce que le registre veut bien livrer. Le périmètre d’un incident devient impossible à borner : faute d’inventaire relié aux déploiements, la question « quels services ont exécuté ce composant ? » se règle par une reconstruction intégrale du parc, plus longue que l’attaque elle-même. Sans verrouillage ni empreinte, rebâtir un binaire réputé sain ne prouve rien, puisque la résolution peut retourner un contenu différent. Sans attestation reliant artefact et commit, l’analyse postérieure ne distingue plus une mise à jour légitime d’une publication hostile, et la date d’entrée du composant reste inconnue. L’organisation qui redistribue ses propres paquets relaie alors la compromission vers ses destinataires, sans pouvoir leur indiquer quelles versions écarter.

Un logiciel utilise souvent du code écrit par d’autres personnes. Ce code peut être téléchargé depuis un dépôt public, ajouté pendant la fabrication du logiciel, puis installé automatiquement sur les machines qui font fonctionner le service. Une attaque de chaîne d’approvisionnement commence lorsqu’un attaquant modifie ce code externe, prend le contrôle du compte qui le publie, ou propose un paquet dont le nom ressemble à celui attendu.

Le logiciel final est alors fabriqué avec un élément hostile sans que la cible soit attaquée directement. Si la mise à jour est acceptée parce qu’elle semble venir d’une source connue, le code hostile peut être installé dans plusieurs organisations. Une signature prouve seulement qu’une clé autorisée a validé le fichier. Elle ne prouve pas que la personne ou le système utilisant cette clé était sain.

Pour limiter ce risque, il faut lister tous les composants présents dans chaque logiciel livré, avec leurs versions. Il faut aussi fixer les versions exactes, vérifier que chaque fichier téléchargé correspond à une valeur prévue, et contrôler qui a signé chaque étape de fabrication. Si une dépendance est compromise, cette liste permet de retrouver les logiciels concernés, de les reconstruire avec une version corrigée et de remplacer les accès qui auraient pu être volés.

À lire aussi

Pages voisines