3 points par GN⁺ 2025-09-20 | 1 commentaires | Partager sur WhatsApp
  • 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

 
GN⁺ 2025-09-20
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 push avec un workflow GitHub Actions malveillant
    Comme 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

    • Pourquoi le token npm d’angulartics2 aurait-il les droits pour publier tinycolor ?
      Ça ressemble vraiment à une voie d’attaque organisationnelle très classique
      Les « péchés passés » finissent toujours par rattraper
      On a eu quelque chose de similaire dans notre organisation il y a quelques années
      Une faille de sécurité restée dans un ancien éditeur remplacé trois fois a fini par permettre des uploads sur le serveur
      On ne l’a pas détectée au moment du build, mais en scannant les URL

    • 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é

    • Entièrement d’accord
      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) restreint
      Mais 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 audit

  • La publication fondée sur une 2FA locale n’est pas viable à long terme
    Pourquoi la publication locale avec 2FA ne serait-elle pas viable ?
    Le vrai problème, c’est le workflow de publication automatisé
    Je ne pense pas que la plupart des packages npm soient publiés si souvent ni qu’ils aient des processus de release si complexes
    Qu’y a-t-il de si difficile à lancer soi-même npm publish avec une 2FA ?
    Si même ça paraît trop pénible, il faut peut-être réévaluer le nombre de packages qu’on maintient

    • C’est un point valable
      Je 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 push

  • La 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