D-OPEN

245 millions de téléchargements piégés — j’ai passé 9 heures à auditer nos Cargo.lock, voici les 4 réflexes que j’ai gardés

Terminal affichant la compilation d’un projet et ses dépendances, illustrant l’exécution de scripts de build
Sophie Nguyen

Sophie Nguyen

Ingénieure infrastructure open source · 22 août 2026 · 12 min de lecture

💡 TL;DR — L’essentiel en 30 secondes

  • • Le 20 août 2026, The Hacker News publie « Rust Supply Chain Attack Puts Build-Time Malware in Crates with 245 Million Downloads ».
  • • Le code malveillant s’exécute au moment de la compilation, via le script build.rs, pas à l’exécution du programme.
  • • Conséquence : vous êtes exposé même si vous n’appelez jamais une fonction du paquet concerné, et même si vous ne livrez rien.
  • • La cible réelle n’est pas votre application, c’est votre machine de développement et vos runners d’intégration continue, où vivent les secrets.
  • • Le fichier qui compte pour l’audit est Cargo.lock, pas Cargo.toml.
  • • Les 4 réflexes retenus : verrouiller, recenser les build.rs, isoler la compilation, faire tourner les secrets.

J’ai lu la dépêche un jeudi matin en pensant y consacrer vingt minutes. J’ai refermé mon terminal neuf heures plus tard, avec deux dépôts internes modifiés et une conviction déplacée sur ce qu’est réellement une dépendance.

Le titre exact, publié le 20 août 2026, est « Rust Supply Chain Attack Puts Build-Time Malware in Crates with 245 Million Downloads ». Trois éléments dans cette phrase méritent qu’on s’arrête : supply chain, build-time, et 245 millions. Le troisième fait les gros titres. C’est le deuxième qui devrait vous empêcher de dormir.

Parce qu’un malware qui s’exécute à la compilation ne suit pas les règles que nous avons intégrées depuis vingt ans sur la gestion des dépendances. Il ne s’active pas quand le programme tourne. Il s’active quand vous tournez.

Pourquoi « build-time » change la nature du risque

Le modèle mental dominant sur les dépendances malveillantes est le suivant : j’importe une bibliothèque, elle contient une porte dérobée, cette porte dérobée s’ouvre quand mon application s’exécute en production. Le raisonnement est confortable parce qu’il implique que la production est la zone de danger, et que le poste du développeur est un environnement de préparation sans enjeu.

Rust, comme beaucoup d’écosystèmes modernes, permet à un paquet de fournir un script exécuté avant la compilation. Cette fonctionnalité existe pour de bonnes raisons — générer du code, détecter des bibliothèques système, compiler du C embarqué. Elle implique aussi que l’ajout d’une dépendance équivaut à accepter d’exécuter du code arbitraire fourni par un tiers, avec vos droits, sur votre machine, avant même que quoi que ce soit ne soit compilé.

Retournons le scénario. Sur un poste de développement, que trouve un script hostile ? Des clés SSH. Des jetons d’accès aux dépôts. Des identifiants cloud dans un fichier de configuration. Des variables d’environnement d’intégration continue. Sur un runner de CI, c’est pire encore : les secrets y sont injectés délibérément, souvent avec des droits d’écriture sur les registres de paquets — ce qui permet à l’attaque de se propager.

Fenêtre d’exécution : porte dérobée classique vs malware de compilation

QUAND LE CODE HOSTILE S’EXÉCUTE-T-IL ?poste devCI / buildartefactproductionPorte dérobée classiqueinerteinerteinerteactifMalware de compilation (build.rs)actifactifpeut être proprepeut être propre▲ Analyser le binaire livré ne détecte rien. Les secrets sont déjà partis.

Ce graphique résume l’essentiel de mon changement d’avis. Toutes nos procédures d’analyse portaient sur l’artefact livré. Aucune ne portait sur ce qui s’était exécuté pour le produire.

Notre avis d’expert n° 1 : le chiffre de 245 millions est une distraction

Le nombre de téléchargements cumulés est le chiffre le plus repris et le moins utile. Il agrège des années d’installations automatiques, de miroirs, de robots d’intégration continue qui retéléchargent la même version mille fois par semaine. Il ne dit rien de votre exposition.

Ce qui détermine votre exposition, c’est l’intersection de deux ensembles : les versions exactes que votre projet a résolues, et la fenêtre temporelle pendant laquelle les versions piégées étaient disponibles. En dehors de cette intersection, vous ne risquez rien, quel que soit le nombre de téléchargements mondiaux.

C’est pour ça que mes neuf heures ont commencé par une commande ennuyeuse : rassembler tous les Cargo.lock de l’organisation, y compris ceux des dépôts d’exemples et des outils internes que personne ne considère comme « du vrai code ». Sur onze dépôts, deux avaient une résolution transitive vers un paquet listé. Aucun des deux ne le mentionnait dans son Cargo.toml.

Notre avis d’expert n° 2 : ce n’est pas un problème de Rust

Chaque incident de ce genre déclenche la même réaction tribale : ceux qui n’aiment pas le langage concerné y voient une confirmation, ceux qui l’aiment minimisent. Les deux ont tort, et l’écosystème Rust est en réalité l’un des mieux outillés pour répondre.

Le mécanisme visé — exécution de code à l’installation ou à la compilation d’une dépendance — existe presque partout. Les scripts de post-installation npm sont le cas d’école. Les fichiers setup.py Python en sont un autre. Les plugins de build Gradle et Maven aussi. Le problème n’est pas linguistique, il est architectural : nous avons construit des chaînes de production où installer et exécuter sont devenus la même action.

La conséquence pratique est libératrice : les réflexes ci-dessous sont transposables tels quels à vos projets Node, Python ou Java. Ne construisez pas une réponse spécifique à Rust, vous la referiez dans six mois pour un autre écosystème.

Vous ne savez pas ce que vos dépendances exécutent pendant vos builds ?

Nous auditons vos chaînes de compilation et vous rendons un inventaire des scripts exécutés, avec un plan de cloisonnement. Discutons-en.

Discutons-en →

Notre avis d’expert n° 3 : votre CI est la vraie cible, et elle est mal défendue

Voici le constat qui m’a le plus dérangée pendant l’audit. Nos runners d’intégration continue sont, de très loin, l’environnement le plus riche en secrets de toute l’organisation — et le moins surveillé. Ils ont accès au registre de paquets, au registre de conteneurs, souvent à des identifiants de déploiement. Ils exécutent, à chaque commit, du code fourni par des centaines de tiers. Et ils n’ont, dans la plupart des équipes, aucune restriction de sortie réseau.

Un script de compilation hostile qui trouve un jeton de publication et l’exfiltre vers un serveur distant boucle la propagation : le paquet suivant qu’il empoisonnera sera le vôtre, signé par vous, distribué à vos utilisateurs. C’est le scénario qui transforme un incident isolé en cascade.

La contre-mesure la plus rentable n’est pas la plus sophistiquée : restreindre les destinations réseau autorisées pendant la phase de compilation. Un build légitime a besoin de joindre le registre de paquets et éventuellement un miroir. Il n’a aucune raison de contacter une adresse arbitraire. Cette seule règle neutralise l’exfiltration, quelle que soit l’ingéniosité du script.

C’est aussi le genre de durcissement que les équipes cyber recommandent depuis des années sans être écoutées ; nos confrères de WebGuard Agency font le même constat sur les chaînes de secrets CI/CD, où l’absence de filtrage sortant reste l’angle mort le plus fréquent en PME.

Les 4 réflexes que j’ai gardés après 9 heures d’audit

Effort de mise en place contre réduction de risque

PRIORISATION DES 4 RÉFLEXES1 · Auditer les Cargo.lock, pas les Cargo.toml2 h2 · Recenser les dépendances avec build.rs3 h3 · Filtrer le réseau sortant pendant le build1 j4 · Faire tourner les secrets exposés2 jLargeur de barre = réduction de risque estimée · libellé = effort

Réflexe 1 — le fichier de verrouillage est la source de vérité. Cherchez dans tous les Cargo.lock, y compris ceux des dépôts que vous considérez comme secondaires. Les dépendances transitives sont invisibles ailleurs.

Réflexe 2 — recensez ce qui s’exécute à la compilation. Sur nos deux dépôts principaux, sur environ 400 dépendances transitives, une quarantaine fournissaient un script de compilation. C’est ça, votre vraie surface d’attaque : quarante, pas quatre cents. Ce nombre est petit, il est auditable, et personne ne le connaissait avant que je le calcule.

Réflexe 3 — cloisonnez la compilation. Filtrage réseau sortant, exécution en conteneur jetable, aucun secret de production monté pendant la phase de build. Cette mesure protège aussi contre la prochaine attaque, dont vous ne connaissez pas encore le nom.

Réflexe 4 — la rotation des secrets est le vrai coût. Nous avons fait tourner tous les jetons présents sur les runners depuis juin. C’est fastidieux, cela casse des choses, et c’était la seule action qui répondait à la question « et si nous avions été touchés sans le savoir ». La difficulté de cette rotation est en soi un indicateur de dette : si faire tourner un secret prend deux jours, c’est déjà un problème indépendamment de tout incident.

Ce que ça dit de notre rapport aux dépendances

Il y a une tentation, après un épisode pareil, de basculer dans l’austérité : réduire le nombre de dépendances, tout réécrire, revenir à la bibliothèque standard. C’est une réaction compréhensible et généralement contre-productive. Le code que vous écrivez vous-même n’est pas magiquement plus sûr ; il est simplement moins audité par des tiers.

La question utile n’est pas le nombre de dépendances mais leur privilège. Combien d’entre elles exécutent du code chez vous avant que vous ne l’ayez demandé ? Combien accèdent au réseau pendant le build ? Combien seriez-vous capable de remplacer en une journée ? Ces trois questions sont mesurables, elles produisent des chiffres, et ces chiffres se réduisent.

Si vous mettez en place une chaîne de compilation propre à cette occasion, notre guide sur la compilation de Rust vers WebAssembly détaille l’isolation de la chaîne d’outils, et notre parcours Comment créer une plateforme SaaS multi-tenant applique la même logique de cloisonnement aux dépendances Python, où les scripts d’installation posent exactement le même problème.

Enfin, pour les équipes qui exploitent des modèles en production et se demandent où placer la frontière de confiance, l’analyse de Plug-Tech sur la sécurité des déploiements IA arrive à une conclusion troublante de similarité : le composant le plus dangereux est toujours celui que personne ne s’attribue.

Questions fréquentes

Qu’est-ce qu’un malware « au moment de la compilation » et pourquoi c’est pire ?

Un paquet Rust peut fournir un script build.rs, exécuté automatiquement par Cargo avant la compilation, avec vos droits, sur votre machine ou votre runner. Un malware placé là s’exécute même si vous n’appelez jamais une fonction du paquet et même si vous ne livrez jamais le binaire. C’est ce qui le distingue d’une porte dérobée classique, active seulement à l’exécution du programme final.

Comment savoir si mon projet a été exposé ?

Le fichier Cargo.lock est la source de vérité, pas Cargo.toml : il enregistre les versions exactes résolues, y compris les dépendances transitives jamais choisies explicitement. Recherchez les paquets concernés dans tous vos Cargo.lock, puis croisez avec les dates de vos compilations pour déterminer si elles tombent dans la fenêtre de publication piégée.

Épingler les versions suffit-il à se protéger ?

Non, cela déplace le problème. Épingler protège d’une version malveillante publiée après coup, mais fige aussi vos correctifs légitimes et ne protège pas si la version épinglée était déjà compromise. La combinaison qui fonctionne : verrouillage par Cargo.lock, mises à jour délibérées et relues, plus une vérification automatisée des paquets qui exécutent du code à la compilation.

Faut-il abandonner les dépendances externes ?

Non. Réécrire soi-même de la cryptographie ou du parsing produit généralement plus de vulnérabilités que d’en importer. La question utile n’est pas combien de dépendances vous avez, mais combien exécutent du code à la compilation et combien vous pourriez remplacer en une journée. Ces deux nombres sont actionnables ; le décompte brut ne l’est pas.

Un doute sur votre chaîne d’approvisionnement logicielle ?

Inventaire des scripts de compilation, cloisonnement des runners, plan de rotation des secrets. On regarde ça ensemble.

Discutons-en →

Source de l’événement : The Hacker News, « Rust Supply Chain Attack Puts Build-Time Malware in Crates with 245 Million Downloads », publié le 20 août 2026. Cet article propose une lecture opérationnelle ; il ne reproduit aucun élément permettant l’exploitation.