Cet article est la transcription de la conférence que j’ai donnée à la Nuit des Communautés en 2026. J’ai repris mes slides et mes notes d’orateur pour en faire un texte suivi. Le talk était en deux parties : l’attaque, puis les enseignements. J’ai gardé ce découpage.
C’est quoi une supply chain attack ?
Vous n’attaquez pas votre cible directement. Vous compromettez un de ses fournisseurs.
- Un attaquant compromet un paquet npm populaire.
- Ce paquet est installé par un développeur ou une CI.
- L’exécution donne accès aux secrets, aux tokens et aux infrastructures cloud.
C’est indirect, ça passe sous les radars, et ça touche potentiellement tous ceux qui installent le paquet.
Shai-Hulud 2.0 — « The Second Coming »
Une première vague, baptisée Shai-Hulud, a eu lieu en septembre 2025. Deux mois plus tard, une seconde version a frappé, plus agressive.
| Métrique | Valeur |
|---|---|
| Paquets npm compromis | 796 |
| Téléchargements hebdomadaires cumulés | 20 millions+ |
| Dépôts GitHub exposés | 25 000+ |
| Secrets exfiltrés | 294 842 |
| Durée de l’attaque | 48 h (21–23 novembre) |
Parmi les victimes notables : Zapier, PostHog, Postman, ENS Domains.
Anatomie technique
Stage 0 — le point d’entrée
Compromettre un premier paquet npm, généralement par ingénierie sociale (phishing d’un mainteneur, prise de contrôle d’un compte).
Stage 1 — le preinstall
Le paquet compromis contient un script preinstall dans son package.json :
{
"scripts": {
"preinstall": "node setup_bun.js"
}
} Pourquoi preinstall ? Parce qu’il s’exécute avant l’installation, et même si l’installation échoue ensuite. Le fichier setup_bun.js est volontairement non obfusqué et bien commenté : il se fait passer pour un installeur légitime du runtime Bun. À la première lecture, le code a l’air normal. L’attaquant joue entièrement sur cette confiance.
Stage 2 — la charge utile
setup_bun.js télécharge et exécute bun_environment.js : un fichier de 10 Mo, lui bien obfusqué. Ce qu’il fait :
- Il énumère les variables d’environnement : tokens npm, GitHub, AWS, Azure…
- Il scanne les fichiers SSH, les
.env, les configs git, l’historique shell. - Il télécharge et exécute Trufflehog — un outil légitime de détection de secrets — pour ratisser tout le répertoire personnel.
- Il regroupe tout dans des fichiers JSON structurés :
cloud.json,environment.json,actionsSecrets.json.
Auto-réplication
S’il trouve un token npm valide, le malware republie les paquets accessibles à ce token, payload inclus. C’est ce qui en fait un ver : il se propage de mainteneur en mainteneur.
Exfiltration
Pas de serveur de commande et contrôle (C2) classique. À la place :
- Création d’un dépôt GitHub public nommé « Sha1-Hulud: The Second Coming ».
- Push des secrets volés dedans.
L’intérêt est redoutable : le trafic vers github.com n’est jamais bloqué par un firewall, il n’y a aucun domaine suspect à repérer, et comme les dépôts sont publics, les attaquants récupèrent les secrets en temps réel. En prime, le malware enregistre la machine victime comme GitHub self-hosted runner — ce qui permet d’exécuter des workflows arbitraires à distance, même après la suppression du paquet.
Le payload destructif
Si le malware ne trouve ni token npm, ni token GitHub — donc rien à voler et rien à propager :
- il détruit le répertoire personnel de l’utilisateur ;
- il écrase puis supprime chaque fichier accessible en écriture.
C’est la première supply chain attack npm dotée d’un mécanisme de sabotage punitif.
Environ 20 % des machines compromises étaient des runners CI/CD : des pipelines entières ont été détruites.
Pourquoi ça nous concerne
On utilise tous npm. Et si vous n’utilisez pas npm, vous utilisez un autre écosystème avec exactement les mêmes mécanismes.
Luck isn’t a security strategy.
— Ryan Sobol
Comment l’attaque a-t-elle été détectée ?
Trois leviers, dans l’ordre où ils ont joué :
- Connaître son écosystème. Savoir quels paquets on installe, qui les maintient, ce qu’un
postinstallest censé faire. - Faire de la veille. Hacker News a relayé l’alerte très tôt.
- S’outiller. Analyse de vulnérabilités dans la CI, Dependabot, Artifactory et registres internes.
Remédiation — les premières heures
Si vous découvrez que vous êtes compromis :
| Action | Détail |
|---|---|
| Ne pas supprimer le dépôt malveillant | Le passer en privé immédiatement pour stopper l’exposition, sans détruire les traces |
| Désactiver les workflows malicieux | Et vérifier tous les self-hosted runners |
| Supprimer les paquets infectés | Revenir aux dernières versions saines connues, surtout sur les registres internes |
| Révoquer TOUS les secrets | Pas seulement ceux que vous avez trouvés — Trufflehog en a peut-être ramassé que vous n’avez pas vus |
| Auditer les accès | Qui a installé quoi, quand, sur quelle machine |
Se protéger
Les basiques
- Des jetons scopés et distincts (poste local vs CI).
- Signature des commits obligatoire.
- Passphrases sur les clés.
- Publier toujours depuis une CI, jamais depuis un poste.
- Utiliser les Trusted Publishers de npm.
Le gestionnaire de paquets est la première ligne de défense
Le comportement par défaut face aux lifecycle scripts varie énormément :
| Gestionnaire | Approche par défaut |
|---|---|
| Yarn (v2, 2020) | Scripts désactivés, activation par dépendance |
| Bun (v1, 2023) | 367 bibliothèques « connues » sur liste blanche, le reste bloqué |
| pnpm (v10, 2025) | strictDepBuilds fait échouer l’installation si un script non approuvé est présent |
| npm | Tout s’exécute, sauf --ignore-scripts configuré à la main |
Gérer les exceptions explicitement
Contrôler quels paquets ont le droit d’exécuter des scripts : enableScripts (Yarn), trustedDependencies (Bun), onlyBuiltDependencies (pnpm). L’avantage : la liste est explicite, approuvée manuellement, et visible en revue de code.
minimumReleaseAge
Bloquer l’installation des versions publiées trop récemment, le temps que la communauté détecte les paquets malicieux :
# .npmrc (pnpm)
minimum-release-age=3d
minimum-release-age-exclude=my-org Les 796 paquets compromis ont été publiés et détectés en 48 h. Un délai de 3 jours les aurait tous bloqués.
Disponible sur pnpm (v10.16, sept. 2025), Yarn (npmMinimalAgeGate, sept. 2025), Bun (v1.3, oct. 2025) et npm (v11.11, 2026).
trustPolicy
pnpm propose trustPolicy: "no-downgrade", qui bloque l’installation quand une nouvelle version d’un paquet présente une authentification plus faible que les versions précédentes.
npm en CI/CD
npm config set ignore-scripts true # désactive tous les lifecycle scripts
npm install --ignore-scripts # au cas par cas En CI/CD, builder systématiquement avec --ignore-scripts et n’exécuter les scripts nécessaires qu’après audit.
Ce qu’il faut retenir
- npm (et ses équivalents) est un vecteur d’attaque majeur.
- Les
preinstall/postinstallscripts sont dangereux par conception. - Les tokens sont la cible n°1 : courts, scopés, protégés par MFA.
- La CI/CD est une surface d’attaque à part entière — traitez les runners comme des machines sensibles.
- La sécurité se joue par empilement de protections, pas par une mesure unique.
- Shai-Hulud 2.0 a introduit le sabotage : l’attaquant détruit ce qu’il ne peut pas voler. Le dev container prend tout son sens.
Pour aller plus loin
- pnpm — How We’re Protecting Our Newsroom from npm Supply Chain Attacks
- Microsoft — Guidance for detecting, investigating and defending
- Wiz — 25K+ Repos Exposed
- Unit 42 — npm Supply Chain Attack
- Check Point — The Most Aggressive npm Supply Chain Attack of 2025
- Datadog Security Labs — npm worm analysis
- Kaspersky Securelist — « Nothing to steal? Let’s wipe. »
- JFrog — Protection & Response Guide
- Snyk — Building a Resilient Software Supply Chain