Que s'est-il passé avec les packages npm sur ces 12 derniers mois ?

Qu’est-ce qu’il s’est passé avec les packages npm durant ces 12 derniers mois ?

Un np**m install sur un projet peut vous faire tirer plusieurs centaines de dépendances en quelques minutes. Ça, on le sait. Mais avez-vous déjà jeté un œil sur les scripts postinstall qui s’exécutent sans votre confirmation et avec vos droits de session ? Jusqu’à maintenant, probablement pas. Jusque septembre 2025, les attaques sur les packages npm se faisaient rares et étaient bien souvent identifiées rapidement. Depuis, la tendance s'est inversée. Les compromissions de paquets sont devenues courantes dans l'écosystème node, et certaines touchent des bibliothèques que vous avez très probablement dans votre lockfile. Parcourons ensemble l’historique des grandes attaques qui ont eu lieu depuis un an et demi et tentons de trouver quelques mesures qui peuvent réduire votre exposition à ces intrusions.

Attention, ce sujet risque de beaucoup bouger. L’'article reflète l'état des lieux au 19 août 2026, et les contre-mesures décrites sont adaptées aux modes opératoires rencontrés jusqu’alors.

Le quotidien des développeur.euses perturbé

Vous démarrez votre journée, vous ouvrez votre projet sur GitHub. Votre pipeline est verte, tout va bien. Le déploiement de la veille s’est bien passé. Le lockfile n'a pas bougé, mais Renovate a ouvert trois PR pendant la nuit et vous les mergez. La CI tourne : npm install, build, tests, déploiement. Vingt minutes plus tard, les patches sont en ligne.

Mais entre-temps, en coulisse, un script postinstall a lu vos variables d'environnement, sorti votre GITHUB_TOKEN, votre clé de déploiement et vos identifiants de registre privé vers un dépôt public créé à la volée. Ensuite, il s'est effacé en laissant derrière lui des procédures propres. Aucune alerte ne vous est remontée. Vous l'apprendrez trois semaines plus tard, par un mail de GitHub.

Ce scénario est, à quelques détails près, le mode opératoire d'une bonne partie des incidents npm des douze derniers mois.

Ce qu’il se passe réellement

Avant septembre 2025, les attaques sur npm relevaient surtout du typosquatting artisanal. Par exemple, on publiait “expres” au lieu de “express”, en espérant une erreur de lecture ou d’import.

Fin août 2025, avec Nx, un script postinstall fouillait la machine à la recherche de tokens (GitHub et npm), de clés SSH et de fichiers d’environnement, puis publiait le résultat de sa recherche dans un dépôt créé sur le compte GitHub de la victime. Cette attaque était innovante en un point. Quand le script trouvait un agent de code comme Claude Code par exemple, il s'en servait pour repérer les fichiers sensibles.

Le 8 septembre 2025, un mainteneur NPM se fait piéger par une attaque de phishing et son compte tombe. Les versions piégées de paquets très populaires partent en production (on parle là de plus de 2.6 milliards de téléchargement hebdomadaires), et portent en eux un intercepteur qui réécrit les adresses de portefeuilles crypto dans le navigateur de l'utilisateur. Une semaine plus tard, Shai-Hulud (nom en référence à Dune) entre en scène. Cette fois-ci, le malware récolte les identifiants npm des machines qu'il infecte et s'en sert pour publier lui-même de nouvelles versions vérolées. In fine, on dénombre 796 paquets compromis (après les deux vagues), pour 132 millions de téléchargements mensuels.

Les incidents s'enchaînent ensuite sans interruption.

Par exemple, le cas axios montre un haut niveau de préparation. L'attaquant a semé sa dépendance malveillante sur npm environ dix-huit heures avant de publier les versions piégées, pour qu'elle n'apparaisse pas comme une nouvelle publication auprès des outils d'analyse. Dans un second temps, il a publié sur la branche courante et sur la branche legacy, en pariant sur le fait que les entreprises qui n'ont pas migré sont aussi les plus laxistes. Enfin, il s’est auto-détruit en se remplaçant par un leurre propre.

En juin 2026, la compromission de l'écosystème Mastra marque une étape supplémentaire. Microsoft a associé l’action à Sapphire Sleet, un groupe étatique nord coréen. Le point d’entrée est le compte d’un contributeur qui dispose des droits de publication sur tout le scope (attaque par ingénierie sociale). À noter que @mastra/core représente près de 4 millions de téléchargements mensuels.

Le but recherché (officiellement) reste le vol de crypto-actifs, mais la procédure mise en place donne un accès aux systèmes d'information de toutes les entreprises qui livrent du JavaScript.

Le 4 août 2026, ChainDrop bat tous les records précédents car en moins de quatre heures, le ver contamine 444 paquets pour environ 2 milliards d'installations mensuelles cumulées, à partir du compte GitHub du mainteneur de keyv et cacheable. Détail qui compte pour la suite : la publication est passée par les pipelines de release légitimes des projets, trusted publishing OIDC compris.

Résumé des principales attaques depuis août 2025 :

Date

Cible

Ce que ça a appris

Source

Août 2025

Nx

Le premier vol massif de secrets via postinstall

Trend Micro

8 sept. 2025

chalk, debug & co.

2,6 Md de dl/semaine en quelques heures

Trend Micro

15 sept. 2025

@ctrl/tinycolor, puis 500+ paquets

autopropagation (ver Shai-Hulud)

Unit 42

Mars 2026

Trivy

76 des 77 tags d'une GitHub Action repointés vers un commit malveillant

Huntress

Fin mars 2026

axios

100 M de dl/semaine, deux branches (1.x actuelle et 0.x legacy) piégées

Trend Micro

Avril 2026

Bitwarden CLI, SAP CAP

Même méthode que pour Trivy, mais la cible sont des paquets d’entreprise, pas ceux d’un mainteneur

Unit 42

17 juin 2026

@mastra

140+ paquets republiés en moins d'une heure, acteur étatique nord coréen

Microsoft Threat Intelligence

Pourquoi s’attaquer à l’environnement node ?

A priori, ce n'est pas une question de langage, puisque Java et Python connaissent des attaques comparables. En revanche, ce qui rend l'écosystème JavaScript particulièrement exposé tient en trois propriétés structurelles :

La granularité. Dans la culture npm on fait des petits modules qui ne font qu’une chose. Cela produit à l'arrivée d’énormes arbres de dépendances. Derrière tous ces packages, on retrouve une multitude de mainteneur.euses à qui on fait confiance par “transitivité”. On notera que la majorité sont des individus non rémunérés pour leur travail et maintiennent sous forme de side-project.

L'exécution à l'installation. Les scripts de cycle de vie (preinstall → install → postinstall) s'exécutent avec les droits de l'utilisateur sur les machines de l'équipe et sur les runners de CI. C’est ce que l’on souhaite, mais ce n’est pas sans risque. La plupart des attaques citées plus haut exploitent cette largesse. Dans le cas Mastra, c’est le hook postinstall d’un des packages (le typosquat easy-day-js) qui est en cause. Il lançait un programme (dropper) chargé de désactiver la vérification des certificats de sécurité, puis téléchargeait et lançait le logiciel malveillant.

Les droits de la CI. Les runners exécutent du code en embarquant les clés de déploiement, les tokens de registre et les secrets dans leurs environnements. Quand un postinstall malveillant tourne sur un poste de dev, il peut voler des identifiants npm. Le même qui tourne sur la CI peut alors voler des accès précieux en prod.

Enfin, lorsque les agents de code ouvrent des PR de mise à jour de dépendances, les bots de veille les mergent quand la CI passe, et en cas de validation humaine, on ne prête pas une attention suffisante. Le délai entre la publication d'une version sur npm et son exécution dans un pipeline est tombé de quelques semaines à quelques heures/jours.

Comment se prémunir ?

Ce découpage recoupe celui proposé après les incidents du printemps 2026.

  1. Résolution : quelle version finit dans le lockfile ?
  2. Installation : quel code s'exécute au moment du install ?
  3. Exécution CI : qu’est-ce que ce code peut atteindre ?
  4. Publication : comment nos propres paquets sont publiés ?

Deux précautions avant d'entrer dans le détail.

Tout d'abord, ces couches ne se remplacent pas. Un lockfile propre ne protège pas d'un postinstall, et --ignore-scripts ne protège pas d'un malware situé dans le code applicatif. Ensuite, aucune préconisations n’est un réel fix au sens strict. En revanche, elles augmentent le coût et la complexité d'une attaque.

Couche 1 : ne pas installer immédiatement une version qui vient de sortir

C'est aujourd'hui le meilleur rapport effort/bénéfice. Puisque les versions compromises ont été détectées et retirées en très peu de temps jusqu’à maintenant, décaler l'installation permet de laisser passer l'orage. Il faut toutefois avoir conscience de la fragilité de ce raisonnement. Il suffit qu’une version vérolée resterait en place sans déclencher d'alerte, pour contourner cette méthode.

Avec pnpm, on utilise minimumReleaseAge, qui prend en compte les dépendances transitives (très appréciable puisque c’est une porte d’entrée importante). Pour information, depuis pnpm 11, la valeur par défaut est de 24 heures, couplée à l’option blockExoticSubdeps. L’équivalent chez npm est min-release-age, et Dependabot propose un cooldown sur les PR de mise à jour.

On peut aussi agir via le réglage trustPolicy: no-downgrade qui bloque l'installation d'un paquet dont le niveau de preuve a baissé : typiquement, une version publiée sans attestation alors que les précédentes venaient d'un éditeur de confiance.

Couche 2 : limiter l'exécution à l'installation

Si on souhaite éviter que les packages exécutent du code sans accord explicite, il suffit de modifier le comportement par défaut. On applique un blocage par défaut et on white-list quelques dépendances seulement. Par exemple :

# pnpm-workspace.yaml

allowBuilds:

- esbuild

- sharp

L’équivalent npm est ignore-scripts=true, avec la contrainte qu'il est global : il faudra réinstaller manuellement ce qui casse.

Une fois de plus, il faut mesurer ce que cette contrainte ne contrôle pas. Elle donne le contrôle à l’équipe sur la faille des postinstall, mais elle n’agit en rien contre du code malveillant placé au sein d’un module.

Couche 3 : ne pas se faire voler dans la CI

Pour éviter que les pipelines d'installation et de build aient dans leurs environnements les secrets dont l'étape de déploiement a besoin, on commence par séparer ces deux groupes de jobs, et on leur attribue des permissions différentes.

Pour aller plus loin, on peut aussi faire jouer les GitHub Actions en fonction des SHA des commits, plutôt que sur des tags de version. La raison derrière est simple : on est capable de rediriger un tag de version, pas un SHA. Ça permet d’éviter les incidents du même type que celui de Trivy.

Couche 4 : ne pas leak vos tokens de publication

Pour information, les tokens permettant la publication sur npm ont évolué. Depuis décembre 2025, ils expirent au bout de 90 jours et exigent la 2FA.

Ce qui paraît être la meilleure solution, c’est le trusted publishing via OIDC, pour que la CI ne stocke plus de token et que chaque version publiée porte une attestation de provenance.

Chez Mastra, l'attaquant a utilisé le compte d'un ancien contributeur, qui avait gardé ses droits et a publié avec un simple token personnel. Pensez donc à retirer les accès inutiles et à interdire la publication par token.

Et si on passe à l’échelle ?

Tout ce qui précède se configure projet par projet. À l'échelle d'une organisation, la même chose s'applique au niveau du registre. Un proxy permet d'imposer le délai avant installation une seule fois, pour tout le monde, avec un double intérêt : ce réglage prime sur la configuration de chaque projet, et il bénéficie aussi aux outils qui ne savent pas gérer ce délai eux-mêmes. Quelques exemples de proxy : Artifactory, Nexus ou CodeArtifact, ou encore Verdaccio avec son plugin de filtrage et son paramètre minAgeDays. Ainsi, au même endroit, vous pouvez bloquer les paquets malveillants et refuser les versions publiées sans attestation de provenance. Attention toutefois, le registre public doit devenir inaccessible autrement, sans quoi un simple .npmrc local suffit à contourner vos règles. Ce registre devient, dès lors, un point de passage obligé pour toutes les installations de l'entreprise.

Ce que tout ça ne protège pas

Un paquet malveillant déjà présent dans vos dépendances depuis six mois passera tous les contrôles décrits ici. Une backdoor introduite par un contributeur qui a obtenu les droits d'écriture aussi. Un malware situé dans le code applicatif plutôt que dans un script d'installation s'exécutera de toute façon, puisque que vous l'importez. Et un attaquant en possession d'un compte de mainteneur, avec ses accès de publication et sa 2FA, dispose des mêmes moyens que son propriétaire légitime (logique). Dernier cas, à part : le mainteneur lui-même. En mars 2022, l'auteur de node-ipc a publié des versions qui écrasaient le contenu des fichiers sur les machines dont l'IP était localisée en Russie ou en Biélorussie pour protester contre l'invasion de l'Ukraine.

Plus tôt dans cet article, j’évoquais la possibilité d’ajouter du délai avant l’installation d’une mise à jour d’un package. Cette technique fonctionne parce que la communauté détecte vite, donc parce que quelqu'un installe la version zéro et lève l'alerte. Si tout le monde se met à attendre, plus personne ne détecte rien (encore logique).

Enfin, et c'est peut-être le point le plus important, les attaquants observent les défenses et s’adaptent d’une attaque à l'autre. Comme depuis toujours dans ce type d’histoires, c’est un bête jeu du chat et de la souris. Il nous faut rendre les attaques coûteuses, pénibles et peu reproductibles pour réduire les risques.

Ce qu'on en retient

Partir du principe qu'un paquet publié sur npm, avec beaucoup de téléchargements, est probablement sain a été raisonnable pendant une quinzaine d'années. On ne peut désormais plus s’y fier aussi facilement. Des groupes organisés se sont aperçus de la facilité et de l'impact de véroler une dépendance.

Les contre-mesures, elles, restent peu coûteuses : quelques lignes de configuration pour le délai avant installation et le blocage des scripts, une séparation des jobs CI, une bascule vers OIDC côté publication.

Le Cyber Resilience Act impose depuis le 11 septembre 2026 de notifier sous 24h les vulnérabilités, avec des sanctions pouvant atteindre 15 millions d'euros ou 2,5 % du chiffre d'affaires mondial.

Ces propositions sont calibrées sur les attaques les plus flagrantes depuis 2025. Elles seront bientôt obsolètes, donc relisez-les après chaque incident marquant plutôt que de les considérer comme acquises.