Principaux enseignements
- « Third Coming » et « Miasma » ont exploité des voies de publication CI fiables plutôt que des jetons de mainteneur à longue durée de vie volés.
- Un registre qui filtre le contenu des publications peut repérer ces publications même lorsque l'attestation est valide.
- Pour les paquets pris en charge par SecureChain, la recompilation à partir d'un code source validé permet d'empêcher qu'une version contenant des identifiants volés ne soit intégrée à votre build.
- Une période de quarantaine n'est utile que si la version malveillante est retirée avant l'expiration du délai.
Les failles de sécurité liées à npm sont suffisamment fréquentes pour que la même question revienne systématiquement. Un registre npm sécurisé aurait-il réellement permis d'éviter celle-ci ? La réponse varie en fonction du mécanisme mis en place, un aspect que la plupart des fournisseurs omettent de mentionner dans leurs déclarations.
Cet article compare les contrôles effectifs de SecureChain à quatre incidents survenus en 2026 : axios, jscrambler, la « troisième venue » de Shai-Hulud et Miasma. Chacun d'entre eux est associé au mécanisme qui le détecte, ainsi qu'au niveau de confiance attribué à cette association.
Deux causes profondes, et non quatre incidents distincts
Si l'on classe ces quatre campagnes en fonction de leur mécanisme, elles se répartissent en deux groupes, et non en quatre.
Les incidents impliquant Axios et Jscrambler ont tous deux été causés par le vol d’un identifiant de publication utilisé directement contre le registre, le processus d’intégration continue (CI) du projet ayant été entièrement contourné. Dans le cas d’Axios, un attaquant a accédé à la machine du responsable principal de la maintenance grâce à une campagne ciblée d’ingénierie sociale, puis a publié manuellement deux versions corrompues le 31 mars. Le dépôt source n’a jamais été modifié, et aucune validation ni balise n’est associée à l’une ou l’autre de ces versions. L’incident Jscrambler survenu en juillet s’est déroulé selon le même scénario. Jscrambler a confirmé par la suite qu’un attaquant avait utilisé un identifiant de publication npm compromis pour publier cinq versions malveillantes en l’espace d’environ trois heures, là encore sans aucune trace de ces versions dans le dépôt.
« Third Coming » et « Miasma » ne correspondent pas à ce schéma, et c’est précisément pour combler cette lacune que cet article a été rédigé, plutôt que de se contenter de renvoyer vers les rapports correspondants. Dans les deux cas, les compromissions ont atteint le pipeline de compilation, et non le jeton npm d’un responsable de maintenance. Pour Bitwarden, l’attaque a été rendue possible par une action GitHub Checkmarx compromise, déjà utilisée dans le propre pipeline de Bitwarden. Par la suite, le workflow de publication a été modifié à plusieurs reprises afin de publier une archive tar précompilée via le chemin de publication npm de Bitwarden, basé sur l’OIDC. Dans le cas de Miasma, au moins 32 paquets relevant du périmètre @redhat-cloud-services de Red Hat ont été diffusés avec des versions malveillantes le 1er juin, après le piratage du compte GitHub d’un employé de Red Hat. L’attaquant a poussé des commits orphelins, qui ont complètement contourné la révision du code, puis les a publiés via le chemin OIDC des GitHub Actions du dépôt. Aucun des deux attaquants n’a eu besoin des identifiants npm d’un responsable pour faire passer une publication pour légitime.
Cette distinction est importante pour l'évaluation. Miasma en est un exemple clair. Wiz a détecté que le workflow injecté demandait un jeton OIDC et publiait des paquets accompagnés d'attestations de provenance SLSA valides, car le workflow qui les avait générés correspondait bel et bien au pipeline d'intégration continue légitime, simplement déclenché par un commit que personne n'avait autorisé.
Le cas de « Third Coming » est plus obscur. Endor Labs a examiné l'historique public des commits et a constaté que le workflow de publication avait été modifié à cinq reprises en peu de temps afin de publier une archive tar par étapes ; lors de l'une de ces tentatives, l'indicateur de provenance avait même été purement et simplement supprimé. L'analyse s'abstient explicitement d'attribuer la publication réussie à une exécution particulière ou d'interpréter ces événements comme une chronologie signée de la réponse à l'incident.
Quoi qu’il en soit, le principe reste valable. Remplacer les jetons à longue durée de vie par le système « Trusted Publishing » est précisément ce qui est recommandé après un incident de vol d’identifiants comme celui d’Axios, et ces deux campagnes montrent les limites de ce conseil, car un pipeline compromis peut produire une publication qui semble légitime, qu’une attestation valide y soit ou non jointe. Un registre qui ne vérifierait que la présence d’une attestation « verte » aurait complètement manqué Miasma, et on ne peut pas non plus supposer qu’il aurait détecté Third Coming.
La tradition des noms s'est également poursuivie. Une campagne distincte a vu le jour en août sous le slogan « Here We Go Again ». Il s'agit d'un événement à part entière, et non d'une répétition de « Third Coming ».
Comment un registre npm sécurisé détecte ce qui parvient jusqu'au pipeline
C'est cet aspect qui est déterminant pour « Third Coming » et « Miasma », car aucun des deux n'est détecté en vérifiant si une publication provient d'un workflow fiable. Les deux impliquaient en effet des chemins de publication CI fiables.
Quand le blocage des scripts d'installation cesse d'être utile
npm exécute automatiquement les scripts d’installation d’un paquet, à moins qu’un élément ne l’en empêche, et c’est le mécanisme auquel la plupart de ces campagnes ont eu recours en premier lieu. La dépendance axios malveillante s’est déclenchée à partir d’un hook « postinstall ». Les trois premières versions corrompues de jscrambler utilisaient le hook « preinstall », tout comme la CLI Bitwarden dotée d’une porte dérobée et la vague principale de versions de Miasma.
Au 1er npm 12, dont la livraison a eu lieu en juillet 2026, les scripts d’installation des dépendances sont désactivés par défaut. Les scripts « preinstall », « install » et « postinstall » ne s’exécutent plus, sauf si un projet les autorise explicitement, et npm bloque également la reconstruction implicite via « node-gyp » qu’un fichier situé au niveau racine binding.gyp fichier utilisé pour déclencher l’attaque. Cette deuxième partie n’est pas purement théorique dans le cas présent, car le rapport de suivi publié par Wiz le 4 juin faisait état d’une nouvelle vague de paquets Miasma exploitant binding.gyp pour exécuter du code pendant l'installation, ce qui correspond exactement à ce qui se passe lorsque les scanners ne lisent que le bloc « scripts ». Sur les anciens clients, --ignore-scripts couvre uniquement le sous-ensemble de scripts « lifecycle » et rien d'autre.
Il y a ensuite cette limite, que jscrambler a mise en évidence dans le cadre de ce même incident. À partir de la version 8.18.0, l'attaquant a déplacé ce même dropper de la phase de préinstallation vers une fonction auto-exécutable située en haut du fichier dist/index.js, ce qui fait qu'il s'est exécuté lors de l'importation ou de l'utilisation via la ligne de commande, et non au moment de l'installation, et tout s'est déroulé sans encombre npm install --ignore-scripts. Les pirates peuvent déplacer la charge utile. Le fait de bloquer les scripts d'installation ne la supprime pas du paquet.
Pourquoi le filtrage de contenu ne tient pas compte de la déclaration sur l'honneur
Le contrôle qui détecte effectivement « Third Coming » et « Miasma » intervient à un stade antérieur et analyse la version plutôt que l'éditeur. L'analyse du contenu permet de repérer des mécanismes d'installation inattendus, du code obscurci dont la présence est injustifiée, des divergences entre l'artefact publié et le code source du projet, ainsi que des anomalies d'une version à l'autre. La version de Bitwarden montre à quel point ce dernier signal peut être flagrant, puisque la comparaison récursive d'Endor Labs a révélé package.json La version 2026.4.0 était annoncée, alors que la CLI fournie avec le logiciel indiquait toujours la version 2026.3.0 dans ses métadonnées compressées, ce qui montre qu’elle avait été reconditionnée bien avant que quiconque ne procède à une décompilation.
Selon la description fournie par SecureChain concernant cette étape, chaque paquet est recompilé à partir du code source dans des environnements sécurisés et isolés, puis analysé à la recherche de logiciels malveillants connus, de modèles de code suspects et de risques liés à la chaîne d’approvisionnement ; tout élément ne passant pas l’inspection est bloqué avant d’atteindre votre environnement. Ce type d’analyse ne tient pas compte du fait qu’une publication provienne d’un jeton personnel piraté ou d’un pipeline CI compromis doté d’une attestation irréprochable, car ce n’est pas l’attestation qui est vérifiée.
Le niveau de confiance est ici modéré à élevé, avec la même réserve que celle qui s’applique au blocage des scripts. Il s’agit d’une détection, et toute détection comporte un taux de faux négatifs. Les logiciels malveillants peuvent retarder, dissimuler ou fragmenter leur comportement, et le pivot de jscrambler en cours d’incident nous rappelle à juste titre que les attaquants s’adaptent à ce que les scanners recherchent. C’est néanmoins l’outil de cette liste qui se rapproche le plus d’une réponse à Third Coming et Miasma selon leurs propres termes, car il évalue le contenu d’une version plutôt que de se fier à l’identité qui se cache derrière.
SecureChain : une alternative au registre npm pour lutter contre les détournements liés au vol d'identifiants
axios et jscrambler constituent le cas le plus simple, et c'est là qu'un substitut au registre npm prend tout son sens, avec un minimum de précautions.
Orientez vos compilations vers un catalogue soigneusement sélectionné et recompilé à partir des sources ; ainsi, pour tout paquet pris en charge par SecureChain, la version que vous obtenez est celle que TuxCare a compilée à partir de sources vérifiées dans un environnement sécurisé, et non la dernière version publiée sur npmjs.com. Un fichier tarball malveillant publié avec un jeton volé ne se trouve tout simplement pas sur ce chemin. Peu importe à quel point son emballage est convaincant ou à quelle vitesse il est signalé ailleurs, car votre compilation n’allait jamais consulter la copie du registre public. La faille de jscrambler concerne structurellement axios sous un nom de paquet différent ; la même garantie s’applique donc aux deux.
Il convient d’apporter des précisions sur deux points, car c’est en les confondant que cet argument est exagéré. Le premier concerne la couverture. SecureChain a été lancé avec les paquets les plus utilisés du registre npm et ajoutera un paquet dont vous dépendez s’il ne figure pas encore dans le catalogue ; la garantie mentionnée ci-dessus s’applique donc aux paquets couverts plutôt qu’à chaque nom que l’arbre de dépendances pourrait inclure. La fidélité de reproduction est le deuxième point. Un paquet npm publié contient souvent un résultat transpilé ou regroupé plutôt que le code source brut du dépôt, et une correspondance octet par octet impliquerait de reproduire l’environnement de compilation propre à l’éditeur, ce que l’écosystème ne garantit pas. Une recompilation SecureChain n’est pas un clone bit à bit de l’archive de l’éditeur, et n’a jamais été conçue pour l’être. Il s’agit d’une compilation dont l’origine est traçable, ce qui constitue une propriété différente et plus utile lorsque c’est précisément l’archive de l’éditeur qui a perdu votre confiance.
C'est précisément à cela que servent les métadonnées associées : assurer cette traçabilité. Les paquets SecureChain sont compilés et signés pour prendre en charge la provenance SLSA de niveau 3 ; leur compilation s'appuie sur des sources vérifiées et s'accompagne de tests continus. TuxCare publie les métadonnées SBOM et VEX lorsqu'elles sont disponibles. La couverture SBOM varie selon les paquets et continue de s'étendre, au format SPDX ou CycloneDX selon le paquet ; il est donc préférable de consulter la documentation actuelle plutôt que de partir du principe qu'il existe un ensemble uniforme.
Une fenêtre de quarantaine est déterministe face à des versions de courte durée
Filtrage, blocage de script, reconstruction à partir du code source. Ces trois processus reposent sur la détection d'une anomalie. Une fenêtre de quarantaine, quant à elle, n'a pas besoin de détecter quoi que ce soit.
Si l’on retarde suffisamment longtemps la résolution d’une version qui vient d’être publiée, une version retirée pendant cette période ne sera jamais éligible pour atteindre le stade de la compilation. Rien n’est analysé, et il n’y a pas de classificateur dont le taux de faux négatifs pourrait être ajusté. La durée de cette période et les éventuelles exclusions restent des choix de configuration, mais ce sont des décisions que l’on peut prendre sur un tableau blanc. Le piège réside toutefois dans la formulation. L'ancienneté à elle seule ne rend pas une version sûre. Elle permet simplement de gagner du temps pour que quelqu'un d'autre la repère. Une version malveillante qui reste en ligne au-delà du délai devient éligible au même titre que n'importe quelle autre version, ce qui est exactement ce qui s'est produit avec jscrambler 8.14.0.
| Incident | Fenêtre d'exposition | Soumis à une quarantaine de 24 heures |
|---|---|---|
| axios | Environ 3 heures, de 00 h 21 à 03 h 15 UTC le 31 mars | Oui, les deux versions ont été retirées bien à l'intérieur de celui-ci |
| Shai-Hulud « La troisième venue » | 93 minutes, du 22 avril à 21 h 57 à 23 h 30 UTC | Oui, c'est bien obsolète à l'intérieur même de celui-ci |
| Miasme | Deux vagues de commits le 1er juin ; la plupart des versions ont été annulées avant 13 h 00 UTC | Probablement, mais cela n'est pas confirmé pour toutes les versions |
| jscrambler | Cinq versions en environ 3 heures, le 11 juillet | En partie, car la version 8.14.0 n'a jamais été retirée du registre |
Deux d’entre elles constituent des victoires avérées de l’arithmétique sur l’analyse. jscrambler en est le contre-exemple. Miasma se situe entre les deux, car Wiz a signalé que la plupart des versions malveillantes avaient été révoquées à 13 h 00 UTC, deux d’entre elles étant encore actives à ce moment-là, ce qui atteste d’un retrait partiel rapide, mais ne permet pas de déterminer si toutes les versions avaient disparu avant l’expiration du délai de 24 heures.
Le compromis est bien réel, lui aussi. Une période de quarantaine implique un léger retard dans l’application des correctifs légitimes, et les équipes qui développent en fonction de dépendances en amont évoluant rapidement en font davantage les frais que celles qui utilisent des plateformes internes plus lentes. C’est également l’une des mesures de contrôle les plus simples à mettre en œuvre ici, et celle qui risque le plus d’être écartée lors d’une évaluation, car elle semble trop simple pour être réellement efficace.
Sécurité d'un registre npm privé : au-delà d'un cache « pull-through »
Le changement n'a d'importance que s'il fonctionne au quotidien ; voici donc la partie opérationnelle.
L'intégration se résume à deux lignes dans .npmrc, une ligne de registre et la ligne d'authentification pour cet hôte, en utilisant le jeton CLN que TuxCare vous a fourni, en remplaçant « YOUR_CLN_TOKEN » par le jeton réel :
registry=https://artifacts.tuxcare.com/npm/
//artifacts.tuxcare.com/npm/:_authToken=YOUR_CLN_TOKEN
package.json ne change pas. Les noms et les versions restent inchangés, et le même point de terminaison dessert les versions SecureChain lorsqu'elles existent, ainsi que le paquet public en amont pour tout le reste ; il n'y a donc pas de deuxième registre à configurer.
Il y a un petit problème qu'il convient de tester avant un déploiement à grande échelle. package-lock.json Il enregistre, entre autres, une URL résolue indiquant la provenance de chaque archive tar ainsi qu’un hachage d’intégrité portant sur les octets de cette archive. Il est normal que l’URL résolue change lorsque l’on change d’hébergeur. C’est le hachage d’intégrité qui présente un intérêt particulier. Un hachage est validé par rapport à un fichier tar dont l’octet est identique, quel que soit l’hébergeur ; ainsi, un simple changement d’hébergeur ne l’affecterait pas, mais une reconstruction de SecureChain n’est, à juste titre, pas identique octet par octet à l’artefact public. C’est cette divergence qui apparaît sous la forme de EINTEGRITY lorsqu'un ancien fichier de verrouillage du registre public se retrouve face à un nouveau registre situé en dessous.
Prévoyez donc un fichier de verrouillage régénéré plutôt qu'une différence purement cosmétique. Supprimez node_modules et package-lock.json, effectuez une nouvelle installation en utilisant le nouveau registre, puis validez le fichier de verrouillage ainsi généré, qui contient les URL SecureChain et les sommes de contrôle. Commencez par effectuer cette opération dans une branche temporaire ou un clone, afin que ce soit vous qui lisiez le diff plutôt que le système d’intégration continue (CI) qui le détecte à votre place. Décidez ensuite dès le départ si la modification doit être intégrée en une seule fois dans tous les dépôts via un commit distinct, ou si elle doit être déployée progressivement. Intégrer un changement de registre dans une branche de fonctionnalités, c’est le moyen le plus sûr de voir la configuration être discrètement annulée par quelqu’un qui ignorait même son existence.
Il suffit de deux étapes supplémentaires pour que cette suggestion devienne une obligation. Déplacer CI vers npm ci partout où il fonctionne encore npm install, puisque npm ci effectue l'installation à partir du fichier de verrouillage validé et échoue lorsque le fichier de verrouillage et package.json refuser la mise à jour plutôt que de l'accepter sans rien dire. Bloquez ensuite l'accès à registry.npmjs.org depuis les agents de compilation, car tant que ce problème n'est pas résolu, le paramètre de registre dans .npmrc Il s'agit d'une suggestion que tout le monde peut ignorer pour débloquer un déploiement.
Testez ce blocage des sorties dans l'environnement de préproduction avant de l'appliquer partout. Les éventuels dysfonctionnements qui y apparaîtront vous permettront d'identifier les pipelines et les outils qui contournent le paramètre du registre ; il est bien plus utile de disposer de cette liste sous forme de document que de devoir la constater à la suite d'un incident.
Arguments en faveur d'un registre npm différent
La vérification du contenu et les processus de reconstruction et de résolution couvrent différents types de défaillances, et non pas deux fois le même. La vérification concerne « Third Coming » et « Miasma », car elle ne dépend pas de la provenance d’une publication. La reconstruction et la résolution à partir d’un catalogue vérifié concernent « axios » et « jscrambler », car pour un paquet couvert, l’artefact malveillant n’entre jamais dans le chemin de résolution de votre build, grâce à des artefacts signés et à une provenance SLSA de niveau 3.
Si votre équipe utilise encore directement le registre public, demandez un devis pour SecureChain pour npm et découvrez ce qu'il en coûterait pour combler cette lacune.
FAQ
Qu'est-ce qu'un registre npm sécurisé ?
Une couche de registre qui fournit des paquets recompilés et évalués pour les dépendances qu’elle couvre, au lieu de transmettre directement l’artefact de l’éditeur. En pratique, il s’agit d’un mélange : les versions SecureChain sont utilisées lorsqu’elles existent, et les paquets publics en amont pour le reste, le tout étant fourni via le même point de terminaison. Cela diffère d’un cache de type « pull-through », qui améliore la disponibilité et la vitesse sans inspecter les éléments qui le traversent.
Un registre npm sécurisé aurait-il pu empêcher la campagne « Third Coming » de Shai-Hulud ?
C'est possible, à condition que le registre analyse le contenu indépendamment de l'identité de l'éditeur. Il n'a jamais été établi si la version malveillante elle-même était accompagnée d'une attestation valide ; on ne peut donc pas se fier uniquement aux vérifications basées sur l'identité dans ce cas précis. Une étape de filtrage consistant à analyser le contenu d’une version (nouvelle version de build, code obscurci, fichiers sans commit correspondant) permet de détecter ces éléments, quelle que soit la teneur de l’attestation. Miasma est l’exemple le plus clair de ce même mode de défaillance, puisque ces versions comportaient bel et bien une attestation SLSA valide et signée.
Une période de quarantaine aurait-elle empêché Axios d'agir ?
Oui. Les versions malveillantes ont été en ligne pendant environ trois heures avant d’être retirées ; ainsi, un délai d’adoption d’une journée — le plus court que npm puisse définir — les aurait rendues inéligibles pendant toute la durée de leur existence malveillante, sans qu’il soit nécessaire de les identifier comme malveillantes. Ce filtre est le paramètre `min-release-age`, qui prend une valeur en jours et nécessite npm 11.10.0 ou une version plus récente, car les clients plus anciens l’ignorent sans le signaler. Il contrôle la résolution des dépendances plutôt que d’agir comme un contrôle rétroactif ; ainsi, une version malveillante déjà épinglée dans un fichier de verrouillage validé constitue un problème distinct.
En quoi un substitut au registre npm change-t-il concrètement le quotidien ?
C'est principalement de là que proviennent les commandes d'installation. package.json et la plupart des processus restent inchangés. package-lock.json doit être régénéré une fois, car l'URL résolue et le hachage d'intégrité qu'elle contient renvoient tous deux à des éléments de l'ancien registre.
La sécurité d'un registre npm privé se limite-t-elle à la simple mise en place d'un registre privé ?
Pas en soi. Un proxy de mise en cache vous offre un point de contrôle sur la provenance des paquets, mais ce point de contrôle ne garantit pas que ce qui le traverse soit fiable. La sécurité effective des installations dépend de ce qui s'exécute au niveau de ce point de contrôle, ainsi que du fait que les voies de contournement soient également bloquées. Le fait que l'intégration continue (CI) continue de se procurer les paquets directement depuis le registre public constitue généralement la faille.
