Aller au contenu

La chaîne d'approvisionnement logicielle

Éditorial

Une application moderne exécute surtout du code que personne dans l'équipe n'a lu, écrit par des inconnus dont certains sont épuisés. SBOM, épinglage, signatures et constructions reproductibles réduisent réellement le risque, mais aucun ne dit si l'auteur est honnête : la porte dérobée de xz-utils l'a montré en passant à travers presque tous.

Mis à jour le 25 septembre 2026~24 min de lecture11 sections

→ Lire l’explication complète de la marmotte

Prenez une application web ordinaire. Quelques milliers de lignes écrites par l’équipe, relues, testées, discutées en réunion avec cette gravité qu’on réserve aux sujets sans importance. Lancez l’installation de ses dépendances. Comptez. Le code que l’équipe a écrit représente désormais une fraction modeste, souvent infime, de ce qui s’exécutera en production. Le reste a été écrit par des centaines de personnes que personne dans l’équipe ne connaît, n’a jamais rencontrées, et dont on ignore jusqu’au pays, à l’âge et à l’humeur du jour.

Nous avons, collectivement, bâti l’essentiel de l’informatique mondiale sur la confiance envers des inconnus. C’est une réussite prodigieuse et, vue sous un certain angle, une imprudence d’une ampleur telle qu’on hésite à la regarder en face, ce qui explique sans doute qu’on la regarde si peu.

Le métier a fini par s’en inquiéter, et il l’a fait comme il fait toujours : en produisant des acronymes. SBOM, SLSA, VEX, attestations de provenance, signatures sans clé. Chacun répond à une vraie question, et chacun a été vendu, à un moment ou à un autre, comme la réponse à la question entière. Cet éditorial soutient que ces outils prouvent la provenance et l’intégrité, et qu’aucun ne prouve la bonne foi. Or la bonne foi est exactement ce qu’une attaque patiente sait contrefaire.

L’affaire de xz-utils, en 2024, en a fait la démonstration avec une netteté presque pédagogique. On y viendra. Il faut d’abord décrire l’arbre.

Une dépendance directe est celle qu’on a choisie : on l’a cherchée, comparée, peut-être même lu sa documentation. Une dépendance transitive est celle que la précédente a choisie pour nous, puis celle que celle-ci a choisie, et ainsi de suite jusqu’à des profondeurs où personne n’est jamais allé. On sélectionne un invité, et c’est toute sa famille qui s’installe au salon, cousins compris.

Mesurer l’arbre est à la portée de tout le monde, et c’est un exercice d’humilité recommandé :

Fenêtre de terminal
# Nombre de paquets réellement installés (lecture seule)
npm ls --all --parseable | wc -l
cargo tree --prefix none | sort -u | wc -l
go list -m all | wc -l

Le chiffre obtenu est presque toujours supérieur à ce que l’équipe aurait parié. Il ne dit rien de la qualité de ces paquets ; il dit seulement le nombre de personnes à qui l’on fait confiance sans le savoir.

Cette confiance a trois formes, qu’il faut distinguer parce qu’elles ne se défendent pas de la même façon.

Confiance dans la disponibilité. Le paquet sera encore là demain. En 2016, le retrait d’un paquet minuscule du registre npm, à la suite d’un différend entre son auteur et le registre, a cassé la construction d’une quantité considérable de projets qui en dépendaient indirectement sans le savoir. Il n’y avait là aucune malveillance, seulement la découverte soudaine qu’une fonction de quelques lignes soutenait une partie de l’édifice, comme une cale oubliée sous une armoire normande.

Confiance dans l’intégrité. Le paquet téléchargé est bien celui que l’auteur a publié. C’est la forme la mieux outillée, parce qu’elle se ramène à de la cryptographie : condensés, signatures, journaux de transparence.

Confiance dans l’intention. L’auteur, et tous ceux qui ont eu le droit de publier en son nom, veulent du bien à vos serveurs. C’est la forme la plus fragile, et la seule que presque aucun outil ne touche.

Il y a un détail que l’on oublie volontiers : dans plusieurs écosystèmes, installer une dépendance, c’est déjà l’exécuter. Les scripts d’installation de npm, les fichiers de construction de certains paquets Python distribués en source, les scripts de construction de Rust s’exécutent sur la machine qui installe, avec les droits de l’utilisateur, avant même qu’une ligne de l’application ait tourné. Le poste du développeur et l’exécuteur d’intégration continue sont donc exposés au moment de l’installation, pas au moment du déploiement.

C’est une cible de choix, car c’est là que traînent les secrets : jetons de publication, identifiants de cloud, clés de signature. Désactiver ces scripts quand on peut (npm ci --ignore-scripts en est un exemple), installer dans un environnement jetable et sans secret, ce sont des mesures modestes et sous-employées. L’éditorial sur les agents le disait d’un autre acteur qui installe volontiers des paquets sans demander : ce qui s’exécute doit s’exécuter là où il n’y a rien à voler.

Avant de corrompre un paquet existant, il est plus simple de faire installer le sien. Deux techniques y suffisent, qui ne demandent ni génie ni accès privilégié, seulement de connaître la distraction humaine et la docilité des outils.

Publier un paquet dont le nom ressemble à celui d’un paquet populaire, à une lettre près, à un tiret près, à une inversion près, et attendre. Il se trouvera toujours un développeur pressé, une documentation mal recopiée ou un script généré qui se trompera de nom. Le paquet contrefait fait souvent ce que fait l’original, en lui déléguant le travail, et ajoute discrètement son supplément. On pense à ces faussaires qui soignent le tableau et bâclent la signature ; ici, c’est l’inverse, et c’est plus efficace.

Les registres se défendent (détection de noms voisins, retrait des paquets signalés), mais la défense est réactive, et la fenêtre entre publication et retrait suffit souvent. Une variante récente mérite une mention : les assistants de programmation fondés sur des modèles de langage inventent parfois des noms de paquets plausibles qui n’existent pas. Quiconque enregistre ces noms à l’avance récolte les installations des programmeurs confiants. On a donc réussi l’exploit de créer une attaque où la victime tape elle-même, sous la dictée d’une machine, le nom de son agresseur.

Plus élégante et plus troublante, la confusion de dépendances a été popularisée en 2021 par un chercheur qui l’a démontrée contre de grandes organisations. Le mécanisme tient en trois phrases. Une organisation utilise des paquets internes, publiés sur un registre privé, sous des noms qui n’existent pas sur le registre public. Un attaquant apprend ces noms (ils fuient dans des fichiers de configuration, des messages d’erreur, des dépôts publics) et publie sur le registre public des paquets du même nom avec un numéro de version plus élevé. Selon la configuration, l’outil d’installation qui consulte les deux registres choisit la version la plus haute, donc celle de l’attaquant.

L’outil ne se trompe pas : il fait exactement ce qu’on lui a demandé, à savoir prendre la version la plus récente parmi les sources déclarées. Le problème est que la notion de « même paquet » repose sur un nom, et qu’un nom n’appartient à personne tant qu’on ne l’a pas réservé. Avec pip, par exemple, déclarer un index supplémentaire à côté de l’index public ne crée aucune priorité entre eux, ce qui surprend toujours ceux qui le découvrent le jour où ils auraient préféré l’ignorer.

Les parades sont connues et ennuyeuses, ce qui est bon signe : un seul point d’approvisionnement (un mandataire interne qui sert à la fois les paquets privés et les copies des paquets publics, avec une règle explicite de priorité), des espaces de noms réservés quand l’écosystème en propose, la réservation défensive des noms internes sur le registre public, et l’épinglage avec condensés, dont on reparlera.

Remontons l’arbre jusqu’à ses racines. Au bout de chaque branche, il y a des gens. Pour beaucoup de composants essentiels, il y a une personne, bénévole, qui maintient le projet sur son temps libre depuis des années, et dont la récompense principale consiste à recevoir des tickets rédigés sur le ton d’une réclamation au service après-vente d’un produit qu’on n’a pas payé.

L’épuisement des mainteneurs n’est pas un sujet de sociologie périphérique. C’est un facteur de risque de premier ordre, pour des raisons mécaniques.

Un mainteneur épuisé publie moins vite les correctifs, ce qui allonge l’exposition aux vulnérabilités connues. Il vérifie moins les contributions, ce qui ouvre la porte aux modifications malveillantes. Il finit par vouloir passer la main, et le transfert de maintenance est le moment le plus dangereux de la vie d’un projet : un inconnu obtient, légitimement, le droit de publier sous un nom auquel des milliers de systèmes font confiance. Aucune signature ne trahira quoi que ce soit, puisque les publications suivantes seront signées par le nouveau mainteneur, en toute régularité.

À cela s’ajoutent les compromissions de comptes (un mot de passe réutilisé, un jeton de publication qui fuit dans un journal d’intégration continue), les domaines de messagerie expirés qu’un tiers rachète pour réinitialiser un compte, et les mainteneurs qui, de leur propre chef, décident un jour de glisser un comportement hostile dans leur code pour protester contre quelque chose. Tous ces scénarios ont en commun de passer par la porte légitime de publication. L’intégrité cryptographique est intacte. C’est l’intention qui a changé.

On cite souvent xz-utils. On le cite rarement pour la bonne raison. L’affaire n’est pas remarquable par sa technique, qui est habile sans être révolutionnaire. Elle est remarquable parce qu’elle a traversé à peu près toutes les défenses dont cet éditorial va parler, et qu’elle a été découverte par hasard.

Je m’en tiens aux faits bien établis. xz-utils est une bibliothèque et un outil de compression présents dans la plupart des distributions Linux. À la fin du mois de mars 2024, un développeur qui enquêtait sur une consommation processeur et une lenteur anormales lors de connexions SSH sur un système de test a remonté la piste jusqu’à la bibliothèque liblzma et découvert une porte dérobée dans les versions 5.6.0 et 5.6.1. Le code malveillant visait le serveur SSH sur certaines distributions, où celui-ci charge indirectement liblzma par le jeu de dépendances ajoutées lors de l’empaquetage. Les versions concernées avaient atteint des branches de développement et de test de quelques distributions, sans avoir encore gagné la plupart des versions stables.

Le vecteur n’était pas une intrusion. Un contributeur, sous pseudonyme, avait participé au projet pendant environ deux ans, gagné la confiance du mainteneur historique, obtenu des droits de publication et fini par préparer les versions. Pendant cette période, d’autres comptes pressaient publiquement le mainteneur de partager la charge, en lui reprochant sa lenteur, alors que celui-ci avait fait état de ses limites de disponibilité. Le code malveillant était dissimulé dans des fichiers binaires présentés comme des données de test, et activé par un script de construction présent dans les archives de publication, mais pas dans le dépôt Git.

Rien de ce qui précède n’exigeait de faille logicielle. Cela exigeait du temps, de la courtoisie, et une connaissance fine de la fatigue humaine.

L’exercice le plus instructif consiste à reprendre les outils de la chaîne d’approvisionnement un par un, et à se demander ce qu’ils auraient dit.

L’inventaire (SBOM) aurait indiqué, avec une parfaite exactitude, « xz-utils 5.6.1 ». C’était vrai. Il aurait été utile après la découverte, pour savoir en quelques minutes qui était concerné. Il n’aurait rien signalé avant.

La signature aurait vérifié que l’archive était bien publiée par un mainteneur du projet. C’était vrai aussi. La signature certifiait l’identité du mainteneur, et c’était précisément le mainteneur qui trahissait.

L’épinglage aurait retardé l’adoption, ce qui n’est pas rien, mais au prochain rafraîchissement des dépendances le paquet serait entré, avec un condensé tout neuf et parfaitement cohérent.

La reproductibilité est la seule qui touchait le vrai mécanisme, et encore, sous une forme précise : la comparaison entre l’archive de publication et le contenu du dépôt au tag correspondant aurait montré un fichier de construction modifié. Dans les projets qui utilisent des outils de génération de scripts de construction, les archives diffèrent légitimement du dépôt, ce qui fournit un bruit de fond idéal pour cacher une différence de plus. Le camouflage n’était pas dans le code ; il était dans une habitude.

Une seule leçon technique nette ressort donc de l’affaire : construire depuis le dépôt, pas depuis l’archive, ou du moins vérifier que l’une se déduit de l’autre. Les autres leçons sont humaines, et plus inconfortables.

Ce que prouvent les outils, et ce qu’ils ne prouvent pas

Section intitulée « Ce que prouvent les outils, et ce qu’ils ne prouvent pas »

Il ne s’agit pas de dénigrer ces outils. Ils répondent à des attaques réelles, et plus fréquentes que la patience d’un xz. Il s’agit de savoir exactement à quelle question chacun répond, pour ne pas lui en poser une autre.

Une SBOM (nomenclature logicielle) est la liste des composants d’un logiciel, avec leurs versions et leurs relations. Deux formats dominent, SPDX et CycloneDX. Sa valeur est considérable le jour où une vulnérabilité est annoncée : au lieu de fouiller chaque projet à la main, on interroge l’inventaire. C’est la même logique que l’inventaire cryptographique : on ne gouverne pas ce qu’on ne sait pas nommer.

Ses limites sont de trois ordres. Elle décrit, elle ne juge pas : une SBOM parfaitement exacte d’un logiciel piégé reste parfaitement exacte. Sa qualité varie énormément selon qu’elle est produite à la construction, à partir de ce qui a réellement été assemblé, ou déclarée après coup à partir de fichiers de manifeste, qui ne disent pas tout (bibliothèques embarquées, code copié, dépendances système). Et elle ne sert que si quelqu’un l’interroge, ce qui suppose un outillage et une habitude qui font défaut plus souvent qu’à leur tour. Une SBOM produite pour satisfaire un questionnaire d’achat et jamais relue est un objet d’art conceptuel : elle représente la sécurité sans jamais y participer.

L’épinglage : empêcher le changement silencieux

Section intitulée « L’épinglage : empêcher le changement silencieux »

Épingler, c’est fixer la version exacte de chaque dépendance, transitives comprises, et idéalement son condensé cryptographique. Les fichiers de verrouillage des gestionnaires modernes le font, et certains outils le rendent obligatoire :

Fenêtre de terminal
# Python : refuser tout paquet dont le condensé n'est pas déclaré
pip install --require-hashes -r requirements.txt
# npm : installer exactement le verrou, sans le modifier
npm ci
# Images de conteneur : référencer par condensé plutôt que par étiquette
podman pull registre.example/equipe/image@sha256:<condensé>

Le gain est réel : une version republiée sous le même numéro, un registre compromis qui sert un autre contenu, un paquet substitué en transit sont détectés, parce que le condensé ne correspond plus. Une étiquette d’image comme latest est une promesse ; un condensé est un fait.

La limite est symétrique : ce qui est figé ne reçoit plus les correctifs. L’épinglage fige indifféremment les bonnes et les mauvaises versions. Il faut donc un processus de mise à jour, souvent confié à des robots qui ouvrent des demandes de fusion à chaque nouvelle version, et l’on retrouve alors le problème par l’autre bout : si ces mises à jour sont fusionnées sans examen, l’épinglage ne fait que transformer un flux continu en flux hebdomadaire. C’est mieux. Ce n’est pas un contrôle.

Signer un artefact prouve qu’il a été produit par le détenteur d’une clé, et qu’il n’a pas été modifié depuis. La difficulté historique n’était pas la cryptographie, bien connue (voir signer numériquement), mais la gestion des clés : qui les détient, comment on les distribue, comment on les révoque, comment on sait qu’une clé appartient bien au projet. La plupart des mainteneurs ne signaient pas, et la plupart des utilisateurs ne vérifiaient pas les rares signatures existantes. L’équilibre était parfait : personne ne faisait rien, et tout le monde était d’accord.

Sigstore a déplacé le problème avec la signature sans clé à longue durée de vie. Le signataire s’authentifie auprès d’un fournisseur d’identité OIDC, reçoit un certificat à très courte durée de vie lié à cette identité (un compte, ou l’identité d’un flux d’intégration continue), signe, et la signature est inscrite dans un journal de transparence public. La vérification ne porte plus sur « cette clé », mais sur « cette identité, émise par ce fournisseur » :

Fenêtre de terminal
# Vérifier qu'une image a été signée par un flux précis d'un dépôt précis
cosign verify \
--certificate-identity "https://github.com/org/projet/.github/workflows/release.yml@refs/tags/v1.2.3" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
registre.example/org/projet@sha256:<condensé>

C’est un progrès considérable. Les attestations de provenance, que plusieurs registres proposent désormais, vont dans le même sens : elles relient un paquet publié au dépôt source et au flux de construction qui l’a produit.

Mais il faut lire la garantie à la lettre, et c’est la lecture que le marketing évite avec une constance admirable. Une signature valide prouve qui a signé. Elle ne prouve pas que ce qui a été signé est honnête. Un mainteneur malveillant signe très bien. Un flux d’intégration continue qui exécute un script piégé produit un artefact piégé, dûment attesté, avec une provenance irréprochable. La provenance dit d’où vient le poison, ce qui est une information précieuse pour l’enquête et d’un réconfort limité pour le patient.

La vérification, elle, doit être stricte ou n’être pas : une politique qui accepte « n’importe quelle identité valide » vérifie seulement que quelqu’un, quelque part, a signé quelque chose. On trouve encore des configurations de ce genre, qui ont l’élégance d’une serrure installée sur une porte qu’on laisse ouverte.

La reproductibilité : savoir que le binaire vient du source

Section intitulée « La reproductibilité : savoir que le binaire vient du source »

Une construction est reproductible quand des personnes indépendantes, partant du même code source et du même environnement déclaré, obtiennent des artefacts identiques au bit près. Le projet Reproducible Builds et plusieurs distributions y travaillent depuis des années, et le travail est ingrat : horodatages, chemins absolus, ordre de parcours des fichiers, variations d’environnement, tout ce qui glisse de l’entropie dans un binaire doit être traqué.

Ce qu’elle apporte est unique : elle retire la confiance nécessaire envers l’infrastructure de construction. Si un constructeur compromis injecte du code dans un binaire, une reconstruction indépendante le révèle. C’est la seule défense sérieuse contre une attaque sur la chaîne de construction elle-même.

Ce qu’elle n’apporte pas : elle garantit que le binaire correspond au source, pas que le source est sain. Un code malveillant commis dans le dépôt se reproduit fidèlement, à l’identique, chez tous ceux qui le reconstruisent. Et, comme on l’a vu avec xz, elle ne protège que si l’on reproduit depuis la bonne source : reproduire fidèlement une archive piégée, c’est confirmer avec rigueur que le piège est bien là.

Les cadres de maturité comme SLSA organisent ces garanties en niveaux progressifs (provenance, isolation de la construction, et ainsi de suite). Ils ont le mérite de dire clairement ce qu’ils couvrent, et de ne pas prétendre couvrir l’intention de l’auteur. Leur contenu a évolué entre versions ; mieux vaut se référer à la spécification en vigueur qu’au souvenir qu’on en a.

La mesure qu’on oublie : avoir moins de dépendances

Section intitulée « La mesure qu’on oublie : avoir moins de dépendances »

Il existe une défense qui ne figure dans aucun acronyme, ne se vend sous aucune forme et ne produit aucun tableau de bord : ne pas dépendre. Chaque dépendance retirée supprime d’un coup tous les risques qu’on vient d’énumérer pour elle et pour sa descendance. C’est la seule mesure dont l’efficacité soit totale sur son périmètre, et c’est naturellement la moins pratiquée, parce qu’elle demande d’écrire soi-même quelques lignes qu’on aurait pu importer.

Le calcul n’est pas toujours favorable. Réécrire une bibliothèque cryptographique ou un analyseur de format complexe pour « réduire la surface d’attaque » est une manière raffinée d’en créer une plus grande. Mais une part notable de l’arbre ordinaire est faite de paquets qui rendent de petits services : une fonction de formatage, un utilitaire de comparaison, un enrobage d’une API standard. Pour ceux-là, la question mérite d’être posée à chaque ajout : ce paquet vaut-il la confiance permanente que j’accorde à son auteur, à ses successeurs et à toute sa descendance ? Posée honnêtement, la réponse est non plus souvent qu’on ne le voudrait, et c’est une réponse gratuite.

On peut aussi préférer, à fonctionnalité égale, les dépendances qui en ont peu elles-mêmes, qui sont maintenues par plusieurs personnes, et qui appartiennent à un projet assez visible pour que d’autres regardent. Aucun de ces critères ne garantit quoi que ce soit. Tous déplacent les probabilités dans le bon sens, ce qui est à peu près tout ce qu’on peut demander à la sécurité.

Voici l’objection la plus forte, et je la crois en grande partie juste : l’obsession de la chaîne d’approvisionnement est un luxe qui détourne des vrais problèmes.

Elle se formule ainsi. Les compromissions spectaculaires de la chaîne d’approvisionnement sont rares, et xz en est l’exemple le plus cité précisément parce qu’il est exceptionnel. Dans l’immense majorité des cas, ce qui fait tomber un système n’est pas une porte dérobée posée par un contributeur patient, mais une vulnérabilité connue, corrigée depuis longtemps, jamais appliquée. Or l’épinglage strict, les vérifications de signature, les politiques d’admission et les revues de dépendances ont un effet secondaire commun : ils ralentissent les mises à jour. À vouloir se protéger de l’attaque improbable, on s’expose davantage à l’attaque banale. Mieux vaudrait investir dans la vitesse de correction que dans la forteresse.

L’argument a un second étage, plus politique. Toutes ces exigences retombent sur les mêmes épaules : celles des mainteneurs. On demande à des bénévoles déjà épuisés de produire des inventaires, de signer, de rendre leurs constructions reproductibles, de répondre à des questionnaires de conformité envoyés par des organisations qui ne leur ont jamais versé un centime. On renforce le maillon le plus faible en lui ajoutant du poids. Si l’épuisement des mainteneurs est un facteur de risque, alors chaque exigence nouvelle qui leur incombe aggrave le risque qu’elle prétend traiter. Il y a là une ironie qui ne devrait échapper à personne, et qui échappe pourtant à beaucoup de cahiers des charges.

Je retiens les deux branches.

Sur la première, la réponse n’est pas de choisir entre vitesse et contrôle, mais de rendre le contrôle compatible avec la vitesse : épinglage avec condensés et mises à jour automatisées, testées, fusionnées vite ; vérification des signatures intégrée à la construction plutôt qu’ajoutée en revue manuelle. L’épinglage n’est pas l’immobilité, c’est le fait de savoir ce qui change quand ça change. Si la mise à jour devient lente, le problème n’est pas l’épinglage, c’est le processus qui l’entoure.

Sur la seconde, je n’ai pas de réponse technique, et je doute qu’il y en ait une. Les organisations qui dépendent d’un composant libre et exigent de lui des garanties devraient en assumer une part du coût : en temps de contribution, en financement, en reprise d’une partie de la maintenance. Ce n’est pas de la philanthropie, c’est de la gestion de risque fournisseur appliquée à un fournisseur qui ne facture pas. Qu’on trouve si normal d’auditer un prestataire payé et si extravagant de soutenir un bénévole indispensable en dit long sur notre rapport à la gratuité : on la consomme avec gratitude, puis on s’étonne qu’elle soit fatiguée.

Je ne sais pas combien d’autres xz existent. Celui-ci a été découvert par hasard, grâce à la curiosité d’une personne qui ne cherchait pas une porte dérobée. On ne mesure pas ce qu’on n’a pas trouvé, et les attaques patientes sont par définition celles qu’on trouve peu. Il peut n’y en avoir aucune autre en service ; il peut y en avoir plusieurs. Je n’ai pas de moyen honnête d’estimer ce nombre, et je me méfie de quiconque avance un chiffre.

Je ne sais pas ce que produiront les obligations réglementaires. L’Union européenne a adopté un règlement sur la cyberrésilience des produits comportant des éléments numériques, qui impose des obligations aux fabricants, avec un traitement particulier pour le logiciel libre et un calendrier d’application échelonné sur plusieurs années. Je ne me risquerai pas à en résumer les dates ni les exceptions ; le lecteur concerné se reportera au texte officiel et aux lignes directrices en vigueur. Qu’un tel cadre finance ou épuise un peu plus les mainteneurs dépendra de détails d’application que je ne connais pas.

Je ne sais pas mesurer l’effet des assistants de programmation sur ce risque. Ils ajoutent des dépendances avec une facilité déconcertante, parfois des dépendances qui n’existent pas encore. Ils pourraient aussi aider à relire les mises à jour, à repérer des différences suspectes entre archive et dépôt. L’effet net m’est inconnu.

Je ne sais pas si la vérification stricte deviendra la norme. Les signatures n’ont de valeur que vérifiées avec une politique précise. L’outillage existe ; l’habitude, beaucoup moins. Cinq ans suffisent parfois à changer une pratique, et parfois vingt ne suffisent pas.

La chaîne d’approvisionnement logicielle n’est pas un problème d’outillage qui attendrait son outil. C’est un problème de confiance envers des personnes, qu’on a outillé là où il était outillable.

Trois choses me paraissent acquises. Les outils prouvent la provenance et l’intégrité, pas l’intention : SBOM, épinglage, signatures et reproductibilité ferment de vraies portes, mais toutes laissent entrer un auteur légitime devenu hostile. Le moment de l’installation est un moment d’exécution : ce qui s’installe doit s’installer là où il n’y a rien à voler. Et le mainteneur épuisé est une vulnérabilité, au même titre qu’un dépassement de tampon, avec cette différence qu’il ne se corrige pas par un correctif.

La porte dérobée de xz-utils n’a été ni détectée par un inventaire, ni bloquée par une signature, ni arrêtée par un épinglage. Elle a été trouvée par quelqu’un qui s’est demandé pourquoi une connexion prenait une fraction de seconde de trop, et qui a eu la mauvaise idée, tout à fait salutaire, de ne pas laisser tomber. Aucun cadre de maturité ne prévoit ce niveau. Il s’appelle la curiosité, il n’est pas certifiable, et c’est peut-être pour cela qu’il fonctionne.

Sans inventaire, sans épinglage ni vérification de signature, il ne faut même plus un auteur hostile et patient : n’importe quelle version republiée, n’importe quel registre compromis, n’importe quel nom mal tapé atteint la construction sans laisser de trace comparable. Deux constructions du même commit peuvent alors embarquer des contenus différents, et personne ne sait dire après coup laquelle tourne en production. Le jour où une porte dérobée est annoncée, l’équipe ne peut prouver ni qu’elle est touchée, ni qu’elle ne l’est pas ; il ne lui reste qu’à tout reconstruire en espérant que la nouvelle récolte vaille mieux que l’ancienne.

Une application moderne s’appuie sur des dépendances : des morceaux de code écrits par d’autres. Chaque dépendance choisie en apporte d’autres, choisies par ses auteurs, et ainsi de suite ; ce sont les dépendances transitives. Dans plusieurs écosystèmes, installer une dépendance exécute déjà du code sur la machine qui l’installe, là où se trouvent souvent des secrets.

Les attaques passent par plusieurs chemins. Un paquet au nom presque identique à un paquet connu attend une faute de frappe. Un paquet public portant le nom d’un paquet interne, avec un numéro de version plus élevé, peut être préféré par l’outil d’installation. Un compte de mainteneur volé, ou un nouveau mainteneur malveillant, publie par la voie officielle.

Chaque outil répond à une question précise. L’inventaire des composants, appelé nomenclature logicielle, dit ce qu’on utilise. L’épinglage fixe la version exacte et son empreinte, une valeur calculée à partir du contenu qui change si un seul octet change. La signature prouve qui a publié. La construction reproductible permet de vérifier que le programme obtenu correspond bien au code source. Aucun de ces outils ne dit si l’auteur est honnête : dans l’affaire de la bibliothèque de compression xz-utils, un contributeur patient a obtenu des droits de publication et caché un code malveillant dans des fichiers de test, activé par un script présent dans l’archive publiée mais absent du dépôt. Réduire le nombre de dépendances reste la seule mesure qui supprime entièrement le risque pour ce qu’on retire.

À lire aussi

Pages voisines