- De nombreux paquets npm incluant le paquet open source @ctrl/tinycolor ont été infectés par des versions malveillantes ; la cause était le vol d’un token npm via un workflow GitHub Actions dans un dépôt collaboratif
- L’attaquant a utilisé un token npm aux permissions étendues pour publier du code malveillant dans environ 20 paquets ; parmi eux, @ctrl/tinycolor avait un impact important avec 2 millions de téléchargements hebdomadaires
- Les versions infectées exécutaient une charge utile malveillante à l’étape postinstall, et les équipes de sécurité de GitHub et npm ont réagi rapidement pour supprimer et nettoyer les versions concernées
- L’auteur a préparé un plan de sécurité renforcé pour éviter une récidive, avec migration vers Trusted Publishing (OIDC), réduction minimale des permissions des tokens, obligation de la 2FA et utilisation de fonctionnalités de pnpm
- Cet incident montre la fragilité de la sécurité de la chaîne d’approvisionnement logicielle et met en évidence la nécessité d’améliorer les fonctions de sécurité et les pratiques de sécurité dans tout l’écosystème npm
TL;DR
- Un workflow GitHub Actions malveillant a été poussé dans un dépôt partagé afin de voler un token npm
- Avec ce token, l’attaquant a publié des versions malveillantes de 20 paquets, dont @ctrl/tinycolor, très diffusé, ce qui a amplifié l’impact
- Les comptes personnels et les dépôts n’ont pas été compromis directement, et il n’y a pas eu de phishing ni d’installation locale de malware
- Grâce à la réaction rapide des équipes de sécurité de GitHub et npm, les versions malveillantes ont été supprimées, puis des versions propres ont été republiées pour purger les caches
Comment je l’ai appris (How I Found Out)
- Le 15 septembre dans l’après-midi, le membre de la communauté Wes Todd a signalé le problème via un DM sur Bluesky
- Les équipes de sécurité de GitHub et npm avaient déjà commencé à établir la liste des paquets affectés et à lancer leur suppression
- Un premier indice partagé était le nom de branche malveillante « Shai-Hulud », tiré du ver des sables de l’univers de Dune
Ce qui s’est réellement passé (What Actually Happened)
- Un collaborateur d’un ancien dépôt commun, angulartics2, disposait encore des droits admin
- Le token npm stocké dans ce dépôt a été volé par un workflow GitHub Actions malveillant
- L’attaquant a utilisé ce token pour publier environ 20 paquets, dont @ctrl/tinycolor
- Les équipes de sécurité de GitHub et npm ont rapidement supprimé les versions malveillantes, puis l’auteur a republié de nouvelles versions de confiance
Impact
- L’installation des versions malveillantes déclenchait un script postinstall, créant un risque de sécurité
- Il est recommandé aux utilisateurs concernés de consulter les consignes de réponse immédiate de StepSecurity
Configuration de publication et plan de réponse provisoire (Publishing Setup & Interim Plan)
- Jusqu’ici, la publication automatique reposait sur la combinaison semantic-release + GitHub Actions
- La fonctionnalité npm provenance était utilisée, mais elle ne pouvait pas empêcher un attaquant disposant d’un token valide
- À l’avenir, l’objectif est d’adopter Trusted Publishing (OIDC) pour supprimer les tokens statiques
- Pour l’instant, tous les tokens ont été révoqués et des mesures de sécurité supplémentaires sont en cours d’application : 2FA obligatoire, autorisation uniquement de tokens à permissions granulaires, examen de la fonctionnalité minimumReleaseAge de pnpm
Améliorations idéales souhaitées (Publishing Wishlist)
- Il faudrait proposer au niveau du compte npm une option imposant Trusted Publishing basé sur OIDC
- Il faudrait pouvoir bloquer la publication en l’absence de provenance et assurer une intégration complète entre semantic-release et OIDC
- L’auteur souhaite une fonctionnalité de publication avec approbation manuelle basée sur la 2FA dans l’interface GitHub
- Il devrait être possible d’utiliser des protections au niveau de GitHub Environments même sans abonnement Pro
- Les pages des paquets npm devraient indiquer la présence d’un script postinstall et rendre public le motif de suppression des versions retirées
1 commentaires
Avis sur Hacker News
Ce dépôt contenait encore un secret GitHub Actions, à savoir un token npm avec des droits de publication étendus
L’un des avantages de Trusted Publishing est justement qu’il n’est plus nécessaire d’utiliser des tokens de publication à longue durée de vie
On n’utilise désormais que des tokens générés temporairement dans la VM CI, valables seulement 15 minutes
C’est déjà en place dans plusieurs écosystèmes comme PyPI, npm, Cargo ou Homebrew
En plus, le processus de publication devient en pratique un peu plus simple, donc je recommande à tout le monde de l’adopter
Si la documentation vous semble encore peu claire, n’hésitez pas à demander de l’aide
Les mainteneurs des écosystèmes souhaitent clairement voir cette fonctionnalité se diffuser largement
Voir la documentation officielle de Trusted Publishing
J’apprends seulement maintenant que Trusted Publishing est désormais possible sur npm
Annonce associée
Je vais configurer ça ce week-end
Ce serait bien d’avoir un indicateur dans le dépôt pour signaler qu’un projet utilise ce type de fonctionnalité
Comme ça, on pourrait facilement bloquer les dépendances qui ne l’utilisent pas
J’ai l’impression que la question d’inclure la MFA (authentification multifacteur) dans le processus de déploiement automatique n’a pas reçu assez d’attention
Publier via un workflow CI tout en confirmant la publication via une invite MFA ne me semble pas poser de problème, mais la dernière fois que j’ai regardé, il fallait ouvrir un tunnel HTTPS pour transmettre le code, ce qui compliquait les choses
J’aimerais que npm ou GitHub proposent directement un moyen simple de fournir et de confirmer un code MFA pendant la CI
La publication d’un package comporte deux étapes : l’upload du package sur npmjs, puis sa mise à disposition réelle aux utilisateurs
Aujourd’hui, ces deux étapes sont fusionnées en une seule opération
À mon avis, il faudrait les séparer : laisser le système CI construire et uploader automatiquement
Puis exiger qu’un humain se connecte directement au site npmjs pour effectuer la publication finale et la MFA manuellement
Je me demande même si le concept même de publication de package est vraiment nécessaire
Si le VCS est la « vraie source », pourquoi ne pas l’utiliser directement sans étape de publication séparée ?
Go fonctionne effectivement comme ça
On importe directement les packages via leur URL, et le versionnement est géré par les tags
Cela réduit la surface d’attaque, puisqu’il suffit de faire confiance au VCS
Pas besoin de comparer des archives séparées, il suffit de vérifier les commits
Le problème, c’est que si le dépôt est déplacé, le chemin d’import change, mais on peut aussi y voir un avantage
À part ça, je ne vois pas très bien ce qu’apporte une étape de publication distincte
Ça ressemble à un vestige de l’époque où on envoyait des archives tar en FTP
J’avais travaillé il y a quelque temps sur un dépôt partagé appelé angulartics2
Il y avait encore là-bas un secret GitHub Actions contenant un token npm avec de larges droits de publication
L’un des collaborateurs avait aussi des droits sur plusieurs projets, ce qui expliquerait probablement pourquoi plusieurs packages ont été touchés en même temps
Une nouvelle branche appelée Shai-Hulud a été
force pushavec un workflow GitHub Actions malveillantComme ce collaborateur avait des droits d’admin, le workflow s’est exécuté directement sans review et le token npm a été exfiltré
Le token volé a servi à publier des versions malveillantes sur 20 packages
La plupart n’étaient pas très utilisés, mais @ctrl/tinycolor est un package populaire téléchargé environ 2 millions de fois par semaine
Ce que je ne comprends toujours pas, c’est comment le token npm du dépôt angulartics2 a pu permettre de publier aussi tinycolor
J’ai aussi des droits d’admin sur le dépôt npm de quelqu’un d’autre, et c’est moi qui ai assuré la plupart des releases récentes
Une fois devenu admin, j’ai commencé à faire plus de commits à mon nom, justement parce que je voulais profiter de l’occasion pour corriger des problèmes laissés de côté depuis longtemps
J’étais presque décidé à passer à la publication de packages via GitHub Actions, mais j’ai toujours eu peur, avec un déploiement manuel en 2FA, de publier par erreur autre chose que l’état de master
À cause de ce genre de problème, j’ai aussi repoussé les discussions avec les autres admins, et vu ce qui s’est passé au final, j’ai l’impression d’avoir bien fait de temporiser
Je ne sais pas quelle est la bonne réponse, mais confier les identifiants à un tiers ne me semble clairement pas être une bonne solution
Désolé si je n’ai pas été assez clair
Ce token avait des droits globaux de publication sur l’ensemble de mes packages npm
Cela fait dix ans que je défends les releases manuelles
J’ai toujours eu beaucoup d’opposition, mais aujourd’hui ça ne paraît plus si étrange
Je sais que le CI/CD est séduisant, mais entre cet incident et les récents problèmes chez CF, on a de plus en plus de preuves que l’automatisation peut au contraire faciliter des problèmes graves
Quand je travaillais autrefois chez BigBank, une mise en production demandait au moins cinq personnes en alerte et énormément de procédures, mais au moins on savait exactement ce qui était déployé
Ce n’est pas à cause de GitHub Actions ou des scripts de release automatiques : je pense simplement qu’assembler, signer, uploader le tarball et vérifier le tout manuellement, comme avant, est bien plus sûr
Les systèmes de distribution, par exemple les systèmes de packaging comme Debian, ajoutent parfois une étape de vérification distincte, ce qui explique aussi pourquoi tout l’Internet n’a pas été compromis lors de l’affaire xz
Il faut au minimum exiger qu’un humain signe effectivement les binaires avant la publication d’une release
Comme un attaquant pourrait aussi s’ajouter comme mainteneur et signer avec sa propre clé, il faut en plus une gestion de clés de confiance, comme dans les systèmes de packaging de distribution
Si mon modèle de menace repose sur « il suffit qu’un compte GitHub ou qu’une clé API fuite pour compromettre tous les utilisateurs », je devrais vraiment me demander si c’est raisonnable
Utiliser la 2FA pour publier, c’est bien, mais ce serait bien plus sûr si plusieurs auteurs devaient approuver via une signature cryptographique
Il ne faut pas qu’une seule compromission suffise à faire réussir l’attaque
Beaucoup de packages n’ont qu’un seul auteur
Exiger les signatures de plusieurs auteurs est une bonne idée, mais si une vérification de signature existait, sous une forme ou une autre, sur les commits, les tags et les artefacts, cela bloquerait déjà la plupart des attaques
Les systèmes de packaging de distribution gèrent très bien la vérification de signature, mais les gestionnaires de packages des langages manquent souvent de mécanismes comparables
Par exemple, le processus officiel de release de runc signe tout avec les clés des mainteneurs, stockées notamment sur des YubiKey
Les systèmes de distribution gèrent aussi leur propre trousseau de clés pour vérifier les sources officielles et les binaires
S’il y avait eu ce type de processus, je pense que cette attaque aurait été stoppée à plusieurs niveaux
On peut construire directement depuis la CI, mais il faut qu’au final le mainteneur signe lui-même
Si les gestionnaires de packages n’offrent pas ce genre de workflow, Trusted Publishing reste l’alternative la moins mauvaise
Mais si le compte GitHub est compromis, par exemple via un vol de cookie, la publication reste immédiatement possible
GitHub permet des réglages de sécurité comme des délais d’expiration pour Trusted Publishing, mais un attaquant pourrait aussi les désactiver
Même si mon compte est compromis, une distribution n’acceptera pas des changements signés avec une clé qui n’est pas la mienne, ce qui est relativement plus sûr
À noter : je travaille chez SUSE, mais j’aimerais voir davantage de support pour la vérification des artefacts dans openSUSE, Arch, Gentoo, etc.
Liens associés :
runc.keyring
keyring_validate.sh
release_sign.sh
runc.keyring d’openSUSE
Je déteste les tokens
Un token, ce n’est au fond qu’un mot de passe statique
Il nous faut une méthode d’authentification plus sérieuse
Par exemple, utiliser GitHub comme fournisseur de tokens pour AWS me paraît déjà aller dans une meilleure direction
Intégration GitHub-AWS via OIDC
Mais ça reste un cas particulier
Les flux OIDC machine-à-machine peuvent être sûrs s’ils sont bien implémentés, mais la configuration est beaucoup trop complexe
Et au final, OIDC donne quand même l’impression de n’être qu’un « token plus compliqué »
Dans un environnement automatisé sans vérification humaine, il y aura toujours quelque part quelque chose qui peut fuiter, que ce soit un token ou un mécanisme de génération de token
Même dans ce cas de ver, OIDC n’aurait pas été une solution de fond
Si le workflow GitHub a été compromis, OIDC ou non, on injecte simplement une identité temporaire dans l’environnement
Au final, ce qui compte, c’est d’empêcher qu’un utilisateur non autorisé puisse exécuter un workflow ayant accès à des secrets
Si l’on veut des permissions fines, réduire la portée des tokens peut même être plus efficace qu’OIDC
L’idée d’origine des tokens, c’est justement d’avoir une durée de vie limitée et un périmètre d’autorisation (
authZ) restreintMais dans la plupart des cas, ce n’est pas du tout ainsi qu’ils sont utilisés, et ils servent simplement de mots de passe statiques
Il existe des alternatives comme OAuth ou les biscuits, qui permettent des restrictions de permissions détaillées, mais elles sont peu utilisées en pratique
Trusted Publishing est désormais pris en charge par plusieurs registres de packages, dont npm
Annonce associée
Comme d’autres l’ont mentionné, les tokens ne devraient être émis qu’avec une courte durée de vie ou après une authentification manuelle, comme la MFA ou une passphrase
Utiliser le mTLS (certificat client TLS) serait probablement la direction la plus proche de la bonne solution
Est-ce que quelqu’un connaît un outil ou un script public pour vérifier les packages npm vulnérables ?
On dirait que la page de stepsecurity ne propose pas ce genre d’outil
Ça ne bloquera pas tout, mais adopter provenance-action est aussi une bonne idée
provenance-action
Pour les problèmes connus, la base reste d’utiliser
npm auditJe voulais surtout dire que je m’inquiétais des erreurs en publication locale, comme publier depuis la mauvaise branche ou oublier un build
Je me demande ce qu’il se passerait si un job CI modifiait l’historique git en profondeur via un
force pushLa situation actuelle ne fonctionne tout simplement plus
Bien sûr, on peut vanter les mérites techniques des tokens OIDC, des solutions zero trust, etc.
Mais parmi les mainteneurs de bibliothèques npm téléchargées des millions de fois, une part importante ne se souciera réellement de la sécurité qu’après un piratage ou après que npm bloque carrément la publication
Et on voit aussi passer des propositions irréalistes du genre « supprimons toutes les dépendances et gardons seulement la bibliothèque standard »
Réduire les dépendances, c’est bien, mais ça ne résout en rien le problème actuel
En pratique, il n’y a que deux options : soit des dizaines ou des centaines de milliers de personnes quittent npm et réécrivent tout leur code, soit npm impose des règles comme la 2FA ou l’OIDC, surtout aux packages les plus téléchargés, et bloque toute publication en cas de non-conformité
Il est assez clair de voir laquelle de ces deux options est réaliste
Sinon, la réputation de npm va toucher le fond, et on finira en plein XKCD 927