Principaux enseignements
- La provenance npm permet de déterminer quel dépôt, quel commit et quel workflow ont donné naissance à un paquet, mais ne garantit pas que le code puisse être exécuté en toute sécurité.
- Le « fossé d'attestation » correspond à l'écart entre ce que prouve la provenance et ce que vous devez réellement savoir avant d'installer un élément.
- Même un workflow détourné ou un commit malveillant génère une traçabilité valide, et la plupart des paquets npm ne comportent aucune traçabilité.
- La recompilation de paquets à partir de sources vérifiées dans un environnement sécurisé éloigne la racine de confiance du pipeline de l'éditeur.
Si vous avez récemment parcouru le site npmjs.com, vous l’avez sans doute remarqué. Au bas de nombreuses pages de paquets, on trouve désormais une section « provenance » indiquant que le paquet a été compilé et signé sur GitHub Actions, avec un lien direct vers le commit d’origine. Cela ressemble à un sceau. On a l’impression que le problème est résolu.
Beaucoup d'équipes considèrent ce label comme la ligne d'arrivée, et honnêtement, je les comprends. Provenance a constitué un véritable pas en avant pour l'écosystème. Mais il répond à une question bien plus restreinte que ce que la plupart des gens imaginent, et c'est précisément dans l'écart entre la question à laquelle il répond et celle qui vous intéresse réellement que se sont produites bon nombre des récentes attaques visant npm.
Ce que « npm provenance » permet réellement de vérifier
npm a introduit la traçabilité en avril 2023, et l’arrivée de la publication sécurisée en juillet 2025 a rendu ce processus quasi automatique. Lorsqu’un responsable de projet publie depuis GitHub Actions via la publication sécurisée, l’interface de ligne de commande npm génère par défaut une attestation de traçabilité, sans qu’aucun paramètre ne soit nécessaire. Cette attestation est une déclaration SLSA signée, enregistrée dans le journal public de transparence de Sigstore, qui relie l’archive tar à un dépôt source, à un commit spécifique et au workflow qui l’a générée. Si vous êtes du côté des utilisateurs, la commande `npm audit signatures` vérifie ces attestations dans l’ensemble de votre arborescence d’installations.
Ce sont des informations utiles à connaître. Avant la mise en place du système de provenance, le lien vers le référentiel figurant sur la page d'un paquet correspondait à ce que le responsable avait saisi dans package.json. Rien ne le confirmait et rien ne le reliait à un commit. Et comme la commande « npm publish » compile tout ce qui se trouve dans le répertoire de travail de l’éditeur, une archive tar pouvait contenir des résultats de compilation, des fichiers épars ou bien pire encore, des éléments qui n’avaient jamais existé dans Git. Pour les paquets qui l’utilisent, la traçabilité comble cette lacune. Vous savez ainsi que l’archive tar provient d’une exécution spécifique de l’intégration continue (CI) liée à un commit précis.
Ce que tu ne sais pas, c’est si quelque chose dans cette série méritait ta confiance.
Le déficit d'attestation
Il est utile de donner un nom à ce phénomène ; je l’appellerai donc le « fossé d’attestation ». Le fossé d’attestation correspond à la différence entre ce que prouve une attestation de provenance (quel référentiel, quel commit et quel workflow de compilation ont généré un paquet) et ce que vous avez réellement besoin de savoir avant de l’installer, à savoir si cet artefact est sûr et s’il a bien été compilé à partir d’un code source que vous n’hésiteriez pas à exécuter.
La provenance concerne l'origine. La sécurité concerne le contenu. Une attestation peut être cryptographiquement parfaite sans pour autant fournir la moindre information sur le comportement du code une fois qu'il se retrouve dans votre node_modules dossier. Si un pirate parvient à introduire son code dans le commit ou dans la compilation, il obtient gratuitement une attestation valide.
Comment un colis peut passer les contrôles de traçabilité et pourtant vous causer des ennuis
La méthode la plus simple consiste à exploiter un responsable de projet compromis. Si quelqu’un prend le contrôle d’un compte GitHub disposant d’un accès « push » à un projet, il n’a pas besoin de toucher à npm. Il effectue un « push » d’un commit, le workflow de publication se déroule exactement comme prévu, et la nouvelle version est diffusée avec une traçabilité indiquant fidèlement le commit de l’attaquant. Tous les contrôles sont validés, car la publication en elle-même ne présente rien d’anormal. Le problème réside dans le commit, et la traçabilité n’a jamais été conçue pour évaluer les commits.
La deuxième approche passe par le pipeline de compilation lui-même. La publication de confiance remplace les jetons à longue durée de vie par des jetons à courte durée de vie générés par le workflow d’intégration continue (CI), ce qui constitue une amélioration considérable. Cela signifie également que l’identité du workflow devient l’identifiant d’authentification. L’analyse d’Andrew Nesbitt sur les failles de sécurité de GitHub Actions dans les paquets Python a mis en évidence des cas où l’attaquant se retrouvait en possession d’un jeton valide généré par le véritable workflow du projet — la seule situation contre laquelle la publication de confiance ne peut rien faire. L'écosystème est différent, mais le mécanisme s'applique tel quel à npm.
La troisième voie est celle que l'on a tendance à oublier. Votre version de publication intègre ses propres dépendances, telles que des outils de regroupement, des transpilateurs et des plugins, qui sont toutes résolues à la volée sur le serveur d'intégration continue. Si l'une d'entre elles est compromise au moment de l'exécution de la tâche de publication, sa charge utile peut se retrouver dans votre dist dossier et l'expédier dans un paquet dont la provenance est irréprochable. L'attestation mentionne votre commit. Elle ne garantit pas les centaines de paquets que votre étape de compilation a installés au cours du processus de création de cette archive tar.
L'absence de provenance est un signal que personne ne prend en compte
Ce qui suit me dérange encore plus. La fonctionnalité « Provenance » est facultative, et la plupart des paquets du registre n’y ont pas souscrit. Elle n’est même pas disponible pour les paquets compilés à partir de dépôts de sources privés. Et npm install installera sans problème une version sans attestation, même si toutes les versions antérieures de ce paquet en possédaient une.
Réfléchissez maintenant au fonctionnement des grandes campagnes de vers informatiques. Shai-Hulud et les vagues qui ont suivi ont ciblé les jetons npm et les identifiants GitHub afin de pouvoir republier automatiquement des paquets légitimes. Une personne en possession d’un jeton volé peut publier depuis n’importe où, ce qui signifie généralement l’absence de test d’intégration continue (CI) et d’attestation de publication. En théorie, c’est un indice révélateur. Lorsque PyPI a rédigé son analyse de la compromission d’Ultralytics, l’équipe a noté que la deuxième vague de publications malveillantes se démarquait par l’absence d’activité correspondante dans le dépôt et d’attestations de publication.
Ça marche si quelqu’un cherche à vérifier la provenance. Dans la plupart des configurations npm, ce n’est pas le cas. Un paquet qui perd soudainement sa provenance ressemble exactement aux milliers d’autres qui n’en ont jamais eu, et votre installation se déroule sans le moindre accroc.
La recompilation à partir du code source comble une autre lacune
Si la provenance vous indique où un logiciel a été compilé, la question qui se pose naturellement ensuite est de savoir si vous pouvez le compiler vous-même et obtenir le même résultat. C’est le principe même d’une recompilation indépendante. Plutôt que de vous fier à l’archive mise en ligne par l’éditeur, vous prenez le code source dont il prétend qu’il provient, vous le recompilez dans un environnement que vous contrôlez, et vous considérez le résultat obtenu comme le logiciel qui sera installé. Le pipeline de l’éditeur cesse alors d’être la source de confiance.
C’est l’approche que nous avons adoptée avec SecureChain. Les paquets sont recompilés à partir de sources vérifiées dans des environnements sécurisés et isolés, sans binaires hérités ni artefacts en amont opaques. Chacun d’entre eux est également analysé à la recherche de logiciels malveillants connus et de modèles de code suspects, et tout élément ne passant pas l’inspection est bloqué avant d’atteindre l’ordinateur portable d’un développeur ou un exécuteur d’intégration continue (CI). Il fonctionne comme un remplacement direct du registre public npm ; ainsi, npm, Yarn et pnpm se comportent tous comme votre équipe s’y attend, et il peut être placé en amont d’Artifactory, de Nexus ou de GitHub Packages si c’est ainsi que vous gérez déjà vos paquets.
SecureChain fournit également une traçabilité SLSA de niveau 3, ce qui pourrait donner l’impression que je contredis mon propre argument. Ce n’est pas le cas. La provenance n’a de sens que dans la mesure où la compilation qu’elle décrit est fiable. La provenance en amont indique que l’infrastructure de continu (CI) du responsable de maintenance a produit un paquet, or une CI compromise peut très bien produire des paquets. La provenance associée à une recompilation indépendante indique que l’artefact provient d’un environnement de compilation sécurisé, à partir d’un code source qui a été vérifié avant d’y être intégré. Il s’agit du même format SLSA dans les deux cas, mais il garantit une compilation complètement différente.
Ce qu’une remise à neuf ne peut pas vous apporter
La recompilation a ses limites, et il convient de les énoncer clairement. Si le code malveillant se trouve dans le code source lui-même, une recompilation fidèle le reproduira à l’identique. C’est pourquoi la recompilation et le dépistage des logiciels malveillants doivent aller de pair. L’une permet de vérifier si l’artefact correspond à son code source, tandis que l’autre permet de déterminer si le code source est un programme que vous souhaitez exécuter sur votre machine. Aucune de ces deux questions ne permet à elle seule de combler cette lacune.
Il existe également un problème à l’échelle de l’écosystème. Tous les paquets npm ne se compilent pas correctement à partir du contenu de leur dépôt. Certains contiennent des fichiers qui n’ont jamais été validés, d’autres dépendent de configurations de compilation que personne n’a jamais documentées, et quelques-uns n’ont tout simplement pas de code source public. Cela pose un problème à quiconque tente de vérifier le registre à grande échelle, et c’est un argument valable pour exiger davantage des éditeurs, et non l’inverse.
Par où je commencerais
La solution la plus économique pour commencer consiste à exécuter la commande « npm audit signatures » dans votre environnement d'intégration continue (CI), afin de pouvoir au moins identifier quelles dépendances sont certifiées et lesquelles ne le sont pas. Si vous publiez vous-même des paquets, optez pour la publication sécurisée afin que vos propres versions soient accompagnées d'une traçabilité. Cela ne vous coûte presque rien et rend l'écosystème un peu plus facile à appréhender.
Engagez ensuite une réflexion plus approfondie avec votre équipe pour déterminer où repose réellement votre confiance. À l’heure actuelle, pour la plupart des projets JavaScript, celle-ci repose sur les pipelines de CI et les comptes npm de milliers d’inconnus, et la traçabilité ne fait que vous indiquer lesquels. Transférer cette confiance vers une source que vous contrôlez, une source qui recompile, filtre et continue de corriger les paquets bien après que leurs responsables de maintenance se soient retirés, constitue une position bien plus solide. C’est précisément là qu’intervient le service « Endless Lifecycle Support » de TuxCare, une fois qu’un paquet arrive en fin de vie.
La provenance est un atout, et j'aimerais que le registre l'utilise beaucoup plus souvent. Elle indique simplement d'où provient un paquet, mais ce qui garantit la sécurité de vos secrets d'intégration continue, c'est son contenu.
