À chaque commande « npm install » est un acte de confiance. L'écosystème de paquets qui accélère le développement JavaScript constitue également un vecteur majeur d'attaques de la chaîne d'approvisionnement. Lorsque vous exécutez une commande d'installation, vous introduisez une arborescence tentaculaire de dépendances, dont beaucoup sont obscures et cachées.
npm install est sans danger la plupart du temps. Ce « la plupart » a une grande importance. L'installation ne se résume pas à un simple téléchargement, c'est une exécution, et les scripts du cycle de vie des dépendances s'exécutent avec les autorisations dont dispose déjà l'environnement d'installation. Sur un ordinateur portable de développeur ou un serveur d'intégration continue (CI), cela suffit généralement amplement. Le code est récupéré, décompressé et exécuté avant même que quiconque ne l'ait examiné. Tout ce qui suit concerne les versions npm v12, pnpm 11 et Yarn 4.10 en août 2026, car les paramètres par défaut dans ce domaine ont évolué rapidement.
Le piège du « typosquatting »
Cette attaque se produit lorsqu'un pirate publie un paquet contenant une erreur typographique. Il peut s'agir d'une simple faute d'orthographe dans le nom du paquet ou d'une astuce de mise en forme tout à fait différente. Ce paquet est pratiquement identique à des paquets populaires et reproduit les mêmes fonctionnalités.
- Manipulation des caractères : Permutation lodash par lodahs, ou en utilisant des homoglyphes (par exemple, en remplaçant le « l » par un « I »).
- Variantes de format : Utilisation d'astuces avec les tirets (cross-env vs. crossenv) ou la mise au pluriel (color vs. colors).
- Confusion entre espace de noms et portée : Création de paquets malveillants à portée limitée imitant les bibliothèques officielles.
Ces paquets peuvent exécuter du code arbitraire pendant le processus d'installation et exfiltrer toutes sortes d'identifiants, qu'il s'agisse de clés API, de variables d'environnement ou d'identifiants AWS, etc. Cela peut se produire extrêmement rapidement, avant même qu'un développeur ait eu le temps d'examiner le code.
Comment fonctionnent les attaques par confusion de dépendances
Cette attaque exploite les organisations utilisant une configuration hybride, c'est-à-dire un registre interne en parallèle du registre public npm.
Les attaques de type « Dependency Confusion » se produisent lorsqu’une entreprise dispose d’un paquet interne et qu’un attaquant en publie un portant le même nom dans le registre public. En général, celui-ci est publié avec un numéro de version supérieur à celui du paquet interne. Si votre environnement de compilation est capable de résoudre ce nom à partir des deux registres, il risque de récupérer le paquet public de l’attaquant à la place. C’est dans ce paquet que se trouve le code malveillant.
Que cela se produise ou non dépend de la configuration de votre registre, de votre scope, de votre proxy et de votre fichier de verrouillage. Les configurations concernées sont celles qui n'ont fait l'objet d'aucun audit.
Pour atténuer ce risque, il convient d'utiliser des paquets à portée limitée et de rendre le routage déterministe. Associez explicitement chaque portée privée à votre registre privé dans le fichier .npmrc, et assurez-vous qu'aucun élément de votre chaîne de proxy ne bascule silencieusement vers le domaine public. Réservez la portée correspondante sur npmjs.com ajoute une deuxième couche de protection, car un attaquant ne peut pas publier sous une portée dont vous êtes déjà propriétaire. Si vous avez installé des paquets au cours des dernières années, vous savez ce qu’est un paquet à portée. Un paquet à portée ressemble à ceci : @my-org/cool-package. Le premier caractère est le symbole @, suivi de votre nom ou de celui de votre organisation, puis du nom du paquet que vous installez.
Sans ces mesures de sécurité, la délimitation seule ne suffira pas à bloquer complètement l'attaque. Une délimitation n'est en soi qu'une convention de nommage, et non un contrôle. S'assurer que votre plateforme CI/CD est correctement configurée contribue également à atténuer le risque.
Que se passe-t-il lorsqu'un compte de responsable est piraté ?
Le « typosquatting » et la « confusion de dépendances » vous obligent tous deux à installer un élément que vous n’aviez pas l’intention d’installer. La version plus sophistiquée fait l’impasse sur cette étape. L’attaquant prend le contrôle d’un paquet auquel vous faites déjà confiance, que vous avez déjà vérifié et que vous avez déjà épinglé.
Il s'agit du vecteur qui cause le plus de dégâts à l'heure actuelle. Une campagne de phishing menée en septembre 2025 contre des responsables de projets npm a donné naissance au ver Shai-Hulud, qui a volé des jetons d'accès au cloud et les a utilisés pour se propager à d'autres comptes. Le 4 août 2026, un pirate a réussi à accéder au compte GitHub du responsable du projet keyv, une bibliothèque enregistrant bien plus de 100 millions de téléchargements hebdomadaires, et a publié des versions destinées à voler des identifiants sur l’ensemble de keyv, cacheable, flat-cache, cache-d’entrée-de-fichier, ainsi que d’autres éléments de ce groupe. Les outils de suivi ont recensé, en l’espace d’une journée, des centaines de noms de paquets concernés et plus d’un millier de versions affectées.
Voici ce qui devrait vous inquiéter. La publication a été effectuée via le workflow GitHub Actions du responsable lui-même ; elle comportait donc une provenance valide certifiée par l’OIDC ainsi qu’un hachage d’intégrité valide. Ces deux éléments décrivaient exactement l’artefact corrompu produit par le workflow légitime. La provenance indique où et comment un paquet a été compilé. Elle ne donne aucune indication sur l’identité de la personne détenant les clés.
Ce sont les outils comportementaux et les contrôles liés à la version et à l'âge qui ont permis de détecter ce problème, et non les vérifications cryptographiques. La traçabilité est utile, mais elle ne constitue pas une fin en soi.
Pourquoi les dépendances transitives ne font-elles pas l'objet d'un audit ?
Une dépendance directe, même si elle a été vérifiée, peut entraîner de nombreuses dépendances transitives que vous n’avez pas remarquées et dont vous n’avez pas conscience. Ces dépendances cachées peuvent se trouver à une profondeur de 5 à 10 niveaux, voire plus, dans l’arborescence des dépendances.
La plupart des dépendances se trouvent au plus profond de l'arborescence du projet, où elles font rarement l'objet d'un audit manuel, créant ainsi une « caverne obscure » entièrement exposée aux attaquants. Ce manque de visibilité constitue un vecteur majeur d'attaques par la chaîne d'approvisionnement. Celles-ci se produisent lorsque des acteurs malveillants infiltrent des paquets ou les injectent intégralement, souvent sans être détectés lors de l'installation — un risque que même certains outils de génération de code basés sur l'IA ne parviennent pas à contrer, comme je l'ai souligné dans mon article de blog sur Dockerfile.
Alors que npm ls est un outil utile pour visualiser l'arborescence complète des dépendances, il ne détecte pas les vulnérabilités ni les comportements suspects. C'est pourquoi les outils d'analyse de la composition logicielle (SCA) sont essentiels pour détecter les problèmes enfouis au plus profond de la pile.
| Outil | Bon en | Il va nous manquer |
| Dépendante | CVE connus dans votre arborescence, demandes de modification (PR) automatisées pour les mises à jour de version | Des paquets malveillants tout nouveaux, car aucun CVE n'existe encore |
| Prise | Signaux comportementaux, nouveaux scripts d'installation, accès inattendus au réseau ou au système de fichiers | N'a pas été conçue dès le départ comme une base de données CVE |
| Snyk | Couverture des vulnérabilités CVE, ainsi que vérification de la chaîne d'approvisionnement et des licences | Il faut régler l'appareil avant que le signal de sortie ne devienne du bruit. |
Dependabot constitue la référence la plus simple à utiliser si vos dépôts se trouvent sur GitHub. Ne le confondez toutefois pas avec un outil de détection de paquets malveillants, car un paquet publié il y a une heure ne dispose pas encore de CVE correspondant.
À quel point ces programmes sont-ils préinstallation et post-installation exécutent du code
préinstallation et post-installation dans le fichier package.json exécutent automatiquement du code une fois qu’un paquet a été récupéré et décompressé, avant que le développeur n’ait examiné son contenu.
Ces scripts ciblent des ressources de grande valeur, telles que les systèmes bancaires, les serveurs de compilation, etc. La crème de la crème, pour ainsi dire. Ils extraient des identifiants essentiels, tels que fichiers .env , les jetons AWS et les clés SSH, dont on nous rappelle sans cesse de les commiter dans le système de contrôle de version.
Certains scripts effectuent des vérifications de l'environnement afin de détecter les environnements de test ou les systèmes faisant l'objet d'une surveillance étroite avant d'exécuter leur charge utile, en ciblant en priorité les environnements moins sécurisés.
Il s'agit toujours du vecteur de diffusion privilégié. Chaque version infectée de la campagne d'août 2026 comportait un un dropper préinstallé.
Les paramètres par défaut ont toutefois fini par changer. pnpm a cessé d'exécuter les scripts de cycle de vie des dépendances par défaut dans la version 10, et npm v12, sorti le 8 juillet 2026, adopte la même approche, gérée via npm approve-scripts. Un script non approuvé est ignoré et génère un avertissement ; il est donc recommandé de définir strict-allow-scripts si vous souhaitez plutôt que l'intégration continue (CI) échoue. Sur npm 11 ou une version antérieure, ce drapeau s'en charge pour vous :
bash npm install --ignore-scripts
Il est bon de savoir que tout cela n'a aucune incidence sur l'exécution au moment de l'importation. Un paquet compromis dont le code de niveau supérieur s'exécute lorsque votre application appelle require() contourne entièrement les contrôles du script d'installation.
Pourquoi l'âge d'un paquet est un indicateur de sécurité
La plupart de ces attaques sont effectivement détectées, généralement en quelques heures plutôt qu'en quelques semaines. Le problème, c'est que votre infrastructure de continuité (CI) a déjà récupéré la version corrompue au cours de ces heures-là.
Un âge minimum de publication réduit cette fenêtre sans que personne n'ait à vérifier quoi que ce soit. Tous les principaux gestionnaires de paquets l'intègrent désormais. pnpm 11 passe à minimumReleaseAge à 1 440 minutes par défaut, tandis que Yarn 4.10 utilise par défaut npmMinimalAgeGate à trois jours par défaut, tandis que npm propose min-release-age à partir de la version 11.10, mais ne l'active pas ; c'est donc à vous de le configurer. Faites attention aux unités, car npm compte en jours et pnpm en minutes.
# .npmrc min-release-age=7
La plupart des professionnels s'accordent sur une période de trois à sept jours. Cela ne résout toutefois pas le problème d'un paquet malveillant dès le départ, ni celui d'un paquet qui reste inactif au-delà de votre seuil de détection.
npx nécessite la même réflexion. Lorsqu’il récupère une nouvelle version, votre fichier de verrouillage ne détermine pas la version choisie ; veillez donc à fixer la version que vous invoquez avec npx [email protected].
Comment installer des paquets npm en toute sécurité
- Veillez à valider vos fichiers de verrouillage et à utiliser npm ci pour obtenir des builds déterministes et fiables dans les environnements de CI. Le fichier de verrouillage associé à npm ci permet de fixer définitivement votre arborescence de dépendances résolues, y compris les dépendances transitives.
- Indiquez les versions exactes dans le fichier package.json en supprimant les symboles de préfixe (^, ~) lorsque vous souhaitez contrôler le moment de la mise à jour des dépendances directes. Cela apporte davantage de prévisibilité que de sécurité, car cela n’a aucun effet sur l’arborescence transitive.
- Configurer le gestionnaire de paquets pour qu’il bloque les scripts de dépendances par défaut. C’est déjà le comportement par défaut sur pnpm 10 et versions ultérieures, ainsi que sur npm v12 ; il s'agit de l'option –ignore-scripts sur les versions antérieures.
- Vérifiez régulièrement vos paquets et utilisez des outils SCA tels que Dependabot, Socket ou Snyk pour détecter les anomalies dans votre arborescence de dépendances.
- Activer un âge minimum de publication compris entre trois et sept jours.
- Activez l'authentification à plusieurs facteurs (MFA) pour chaque compte autorisé à publier, et veillez à ce que les jetons de publication aient une portée restreinte et une durée de vie limitée.
Le blocage des scripts a un coût réel, alors réfléchissez-y bien avant de prendre une décision. Les modules natifs et les outils d'automatisation des navigateurs nécessitent des étapes d'installation, et une approche rigoureuse, bcrypt, Prisma, Playwright et Cypress en font généralement les frais. En intégration continue (CI), bloquez-les par défaut, n’autorisez que ce dont vous avez besoin et exécutez ces builds sur un environnement qui ne contient pas vos identifiants de production. Sur un ordinateur portable, autorisez le paquet qui pose problème plutôt que de désactiver la protection pour tout. Dans tous les cas, surveillez les échecs silencieux, car un build natif bloqué peut se terminer sans erreur et ne se révéler que plus tard sous la forme d’un module manquant lors de l’exécution.
Réflexions finales sur la sécurité de la chaîne d'approvisionnement de npm
La sécurité de la chaîne logistique logicielle n'est jamais une exigence opérationnelle ponctuelle. La sécurité est un processus continu qui doit toujours être une priorité absolue. Les trois aspects souvent relégués au second plan jusqu'à ce que la situation devienne critique sont les performances, l'accessibilité et – vous l'avez deviné – la sécurité. Se tenir informé des outils et des astuces permettant de garantir la sécurité fait partie intégrante du métier de développeur. Gardez à l'esprit les mesures de protection concrètes et placez toujours la sécurité au premier plan lors de la mise en production.

