En 2026, npm install n’est plus une simple commande : c’est l’exécution de code tiers sur une machine qui a de la valeur. Depuis septembre 2025, des paquets téléchargés des milliards de fois par semaine ont été compromis, des vers ont volé des jetons pour se republier eux-mêmes, et la bibliothèque HTTP la plus utilisée de l’écosystème a distribué un cheval de Troie pendant quelques heures. npm, pnpm et Yarn ont réagi en changeant leurs réglages par défaut. Voici ce qui s’est passé, ce qui a changé, et les sept réglages que nous appliquons sur nos projets JavaScript et TypeScript.

Ce qui s’est réellement passé depuis 2025
8 septembre 2025 : chalk, debug et 16 autres paquets. Un mainteneur très suivi reçoit un faux courriel de « mise à jour de la double authentification » envoyé depuis le domaine npmjs.help. L’attaquant récupère ses identifiants et un code à usage unique, puis publie en quelques minutes des versions piégées de 18 paquets, dont chalk et debug, qui cumulent environ deux milliards de téléchargements par semaine. Le code injecté détournait des transactions en cryptomonnaie dans le navigateur. Les versions ont été retirées en quelques heures, mais toute installation « fraîche » pendant cette fenêtre les récupérait.
Mi-septembre 2025 : Shai-Hulud, le premier ver npm. À partir du 15 septembre, un code malveillant diffusé via un script postinstall vole les jetons npm, GitHub et cloud présents sur la machine, puis republie automatiquement des versions infectées de tous les paquets que la victime peut publier. L’alerte de la CISA, le 23 septembre, fait état de plus de 500 paquets compromis.
24 novembre 2025 : Shai-Hulud 2.0. Une deuxième vague passe par un script preinstall, exécuté avant même la fin de l’installation. Environ 800 paquets sont touchés et plus de 25 000 dépôts GitHub reçoivent des secrets volés.
31 mars 2026 : axios. Le compte du principal mainteneur d’axios est compromis. Deux versions (1.14.1 et 0.30.4) sont publiées avec un jeton volé, en dehors du pipeline habituel. Le code d’axios n’a pas changé : seul le package.json ajoute une dépendance, plain-crypto-js, dont le script postinstall installe un cheval de Troie sur macOS, Windows et Linux. Les versions restent en ligne environ trois heures.
Juin 2026 : des paquets de Red Hat. Selon le bulletin de Red Hat, un compte GitHub compromis via une extension VS Code malveillante a servi à injecter du code dans des paquets publiés sous l’espace @redhat-cloud-services.
Les points communs sont nets : un compte ou un jeton de mainteneur compromis, un script exécuté à l’installation, des secrets volés sur les postes et dans l’intégration continue, et une fenêtre d’exposition de quelques heures.
Ce qui a changé dans l’outillage
- Jetons npm : depuis le 9 décembre 2025, les jetons « classiques » sont révoqués. Les jetons d’écriture ont une durée de vie limitée, la double authentification est requise par défaut et
npm loginouvre une session de deux heures. - Publication de confiance (trusted publishing) : un pipeline GitHub Actions ou GitLab CI peut publier sans jeton stocké, via OIDC, et la provenance du paquet est attestée.
- npm v12 (juillet 2026) : les scripts d’installation des dépendances ne s’exécutent plus sans autorisation explicite, et les dépendances Git ou par URL distante sont refusées par défaut.
- Délai minimal de publication : npm (depuis la version 11.10, option
min-release-age, en jours), pnpm (minimumReleaseAge, en minutes, un jour par défaut depuis pnpm 11) et Yarn (npmMinimalAgeGate) peuvent refuser les versions trop récentes.
Ces protections ne servent que si elles sont activées et si les outils sont à jour. L’épisode axios le montre : la publication de confiance était en place, mais l’attaquant a publié à la main avec un jeton encore valide.
Sept réglages concrets
1. Installer depuis le fichier de verrouillage
En intégration continue et pour les builds de production, npm ci installe exactement les versions du package-lock.json et échoue s’il n’est pas cohérent avec le package.json.
npm ci
Le fichier de verrouillage est relu en revue de code, comme le code applicatif.
2. Bloquer les scripts d’installation
Avec npm v12, c’est le comportement par défaut. On autorise explicitement les seuls paquets qui en ont besoin (compilateurs natifs, par exemple), et la liste est enregistrée dans le package.json :
# lister les paquets dont les scripts ont été bloqués
npm approve-scripts --allow-scripts-pending
# autoriser explicitement un paquet
npm approve-scripts esbuild
Sur npm 11, le réglage équivalent se place dans le .npmrc du projet :
ignore-scripts=true
pnpm bloque aussi les scripts des dépendances par défaut ; la liste blanche se déclare avec allowBuilds dans pnpm-workspace.yaml.
3. Imposer un délai minimal de publication
La plupart des versions malveillantes sont détectées et retirées en quelques heures. Refuser les versions de moins de quelques jours évite de les installer.
# .npmrc (npm 11.10 ou plus) : délai en jours
min-release-age=3
# pnpm-workspace.yaml : délai en minutes
minimumReleaseAge: 4320
minimumReleaseAgeExclude:
- '@votre-organisation/*'
# .yarnrc.yml (Yarn 4) : délai en minutes
npmMinimalAgeGate: 4320
Pour un correctif de sécurité urgent, on fait une exception ciblée et documentée, pas une désactivation globale. Pensez aussi à régler le même délai dans Dependabot ou Renovate, sinon ils proposeront des mises à jour que l’installation refusera.
4. Vérifier signatures et provenance
npm audit signatures
La commande vérifie les signatures du registre et les attestations de provenance des paquets installés. Une version d’un paquet habituellement publié avec provenance qui arrive sans elle, comme pour axios en mars 2026, doit alerter.
5. Publier sans jeton stocké
Si vous publiez des paquets, passez à la publication de confiance. Côté GitHub Actions, le job a seulement besoin de la permission OIDC :
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
registry-url: https://registry.npmjs.org
- run: npm ci
- run: npm publish
Le paquet et le dépôt autorisé se déclarent ensuite dans les réglages du paquet sur npmjs.com. Supprimez les anciens jetons de publication.
6. Auditer en continu
npm audit --audit-level=high
Branchez l’audit dans l’intégration continue, activez les alertes de sécurité de votre forge (GitHub, GitLab) et automatisez les mises à jour avec une revue humaine.
7. Limiter les secrets exposés à l’installation
Pas de jetons à longue durée de vie sur les postes, des secrets d’intégration continue limités au job qui en a besoin, des runners éphémères. Et si une version compromise est passée, un contrôle rapide du fichier de verrouillage, comme celui recommandé après l’incident axios :
grep -E "axios@(1\.14\.1|0\.30\.4)|plain-crypto-js" package-lock.json
Si la recherche renvoie un résultat, la machine est à considérer comme compromise : renouvelez tous les secrets auxquels elle avait accès.
Ajouter une dépendance est une décision d’architecture
Les réglages techniques ne remplacent pas le jugement. Avant d’ajouter une dépendance, quatre questions :
- Doublon ? Une bibliothèque équivalente est-elle déjà dans le projet ?
- Maintenance ? Le dépôt est-il actif, les failles corrigées, les mainteneurs identifiés ?
- Installation ? Le paquet exécute-t-il des scripts, et pourquoi ?
- Périmètre ? Quelques lignes de code interne suffiraient-elles ?
Tester une nouvelle bibliothèque se fait dans un projet séparé, sans secrets. Elle n’entre dans le vrai package.json qu’une fois le besoin confirmé.
Dernier point, nouveau en 2026 : les agents de code IA installent eux aussi des paquets. Les mêmes règles s’appliquent à eux, avec des permissions encore plus strictes.
Par où commencer
Si vous avez un dépôt JavaScript ou TypeScript avec des dizaines de dépendances, des pipelines d’intégration continue critiques ou une production cloud où tout passe par npm :
- mettez à jour npm, pnpm ou Yarn et activez le délai minimal de publication ;
- passez l’intégration continue à
npm ciavec les scripts bloqués par défaut ; - inventoriez les jetons et secrets accessibles au moment de l’installation, et réduisez-les.
Chez Etixio, ces réglages font partie de notre standard d’ingénierie : revue de code systématique (fichier de verrouillage compris), tests automatisés, intégration continue, gestion des secrets, environnements séparés. Nous les mettons en place dans vos dépôts et vos outils, avec vos règles.
Vous voulez faire le point sur vos dépendances et votre chaîne de build ? Nous pouvons commencer par un audit technique ou l’intégrer à un programme cybersécurité et conformité. Parlons-en.
FAQ
Pourquoi npm install est-il un risque de sécurité ?
Parce qu’il télécharge et, selon la configuration, exécute du code tiers sur la machine qui le lance : poste de développeur, serveur d’intégration continue, image de production. Les scripts d’installation (preinstall, postinstall) peuvent lire les variables d’environnement, les clés SSH ou les jetons présents. C’est ainsi qu’ont opéré les attaques Shai-Hulud en 2025 et la compromission d’axios en 2026.
Quelle différence entre npm install et npm ci ?
npm install résout les versions à partir du package.json et peut mettre à jour le fichier de verrouillage. npm ci installe exactement les versions du package-lock.json, échoue s’il n’est pas cohérent avec le package.json, et repart d’un dossier node_modules vide. En intégration continue et pour les builds de production, utilisez npm ci.
Qu’est-ce qu’un délai minimal de publication ?
C’est un réglage qui empêche d’installer une version publiée depuis moins de N jours (npm, option min-release-age) ou N minutes (pnpm, minimumReleaseAge ; Yarn, npmMinimalAgeGate). Les versions malveillantes sont en général détectées et retirées en quelques heures : un délai d’un à quelques jours évite la plupart d’entre elles. pnpm 11 applique un délai d’un jour par défaut.
npm v12 bloque-t-il les scripts d’installation ?
Oui. Depuis npm v12 (juillet 2026), les scripts d’installation des dépendances ne s’exécutent plus sans autorisation explicite, et les dépendances Git ou par URL distante sont refusées par défaut. On autorise les paquets nécessaires avec npm approve-scripts, et la liste est enregistrée dans le package.json.
Que faire si une dépendance compromise a été installée ?
Considérer la machine comme compromise : isoler le poste ou le runner, révoquer et renouveler tous les secrets accessibles (jetons npm et GitHub, clés cloud, clés SSH), vérifier les dépôts et les paquets publiés depuis ces accès, puis réinstaller à partir d’un fichier de verrouillage corrigé. Supprimer le paquet ne suffit pas.


