Das rätselhafte Supply-Chain-Risiko des npm-Pakets string-width-cjs
3. Oktober 2024
0 Min. LesezeitDiese Geschichte beginnt damit, dass Sébastien Lorber, Maintainer des React-basierten Open-Source-Dokumentationsprojekts Docusaurus, eine Änderung am Paketmanifest in einem Pull Request bemerkt. Hier sehen Sie die vorgeschlagene Änderung am beliebten npm-Paket cliui:

Besonders fällt die Änderung an den npm-Abhängigkeiten auf, die eine ungewohnte Syntax verwendet:
Die meisten Entwickler erwarten im Wert eines Pakets einen semver-Versionsbereich oder vielleicht eine Git- oder dateibasierte URL. In diesem Fall kommt jedoch die spezielle Präfixsyntax npm: zum Einsatz. Was bedeutet sie?
Was ist npm-Paket-Aliasing?
Der npm-Paketmanager unterstützt eine Alias-Funktion, mit der sich benutzerdefinierte Auflösungsregeln für Pakete festlegen lassen. Wird das Paket an anderer Stelle referenziert – im Code oder in der Lockdatei –, wird es daher in den Namen und die Version aufgelöst, die durch den Alias angegeben sind.
Im Fall der Änderung in diesem Pull Request wird das Paket string-width-cjs also auf das Paket string-width in den Versionen ^4.2.0 aufgelöst. Das bedeutet, dass es im Verzeichnis node_modules einen Eintrag für string-width-cjs gibt, der jedoch den Inhalt von string-width@^4.2.0 enthält. Dasselbe gilt für die Lockdatei (package-lock.json).
Das Paket-Aliasing ist eine Funktion des npm-Paketmanagers, die beispielsweise zur Unterstützung von ESM und CJS eingesetzt werden kann.
Allerdings lässt sich Paket-Aliasing auch missbrauchen. In einem Artikel und einer Sicherheitsmeldung aus dem Jahr 2021 zeigte Nishant Jain, ein Snyk Ambassador, wie sich die offizielle npmjs-Registry durch Paket-Aliasing dazu bringen ließ, falsche Abhängigkeitsinformationen anzuzeigen – ein Risiko für Dependency Confusion und die Supply-Chain-Sicherheit.
Der Pull Request war harmlos und es bestand kein Risiko eines Supply-Chain-Angriffs. Sébastien beunruhigte jedoch der Paketname, was zur Entdeckung eines potenziellen Sicherheitsrisikos führte.
Verdächtiges Verhalten in npm-Lockdateien: Hinweise auf bösartige Module
Zur Untersuchung des Pull Requests verwendete Sébastien lockfile-lint. Dieses Tool überprüft Lockdateien wie package-lock.json oder yarn.lock auf Manipulationsanzeichen und stellt sicher, dass keine bösartigen Pakete anstelle des ursprünglichen npm-Pakets eingeschleust wurden.
Beim Ausführen des Tools wurden folgende Warnungen angezeigt:
Hinweis: lockfile-lint ist ein Tool, das ich 2019 entwickelt habe, nachdem ich den Sicherheitsvorfall mit Lockdateien veröffentlicht hatte: Warum npm-Lockdateien beim Einschleusen bösartiger Module ein Sicherheitsblindfleck sein können.
Alarmstufe Rot: beliebte Paket-Imitate auf npm
Aufgrund der oben genannten Ergebnisse von lockfile-lint suchte Sébastien auf npm nach diesen Paketnamen und stellte überrascht fest, dass sie tatsächlich in der öffentlichen npm-Registry vorhanden sind:
https://www.npmjs.com/package/string-width-cjs
https://www.npmjs.com/package/strip-ansi-cjs
https://www.npmjs.com/package/wrap-ansi-cjs
Sébastien stellte fest, dass diese Paketnamen nicht nur auf npm existieren, sondern auch verdächtige Merkmale aufweisen. Die Pakete waren keinem öffentlichen Quellcode-Repository zugeordnet, enthielten bei der Überprüfung keinen tatsächlichen Code und wurden anonym ohne persönliche Angaben veröffentlicht.
Beim npm-Paket strip-ansi-cjs gibt es weder eine README-Datei noch ein Quellcode-Repository. Allerdings verweisen viele legitime und beliebte Pakete auf dasselbe Verhalten.
Tatsächlich ist dieses Paket beliebt, wie die 529 abhängigen Pakete und 7.274 wöchentlichen Downloads zeigen.

Ein Blick auf den Code von strip-ansi-cjs zeigt, dass dieses Paket nur eine einzige Datei enthält: das Paketmanifest package.json.
Warum wird ein Paket, das nichts tut, so oft heruntergeladen, und warum hängen so viele andere Pakete davon ab?

Sehen wir uns die Autorenschaft dieser npm-Pakete an.
Alle drei Pakete gehören himanshutester002 und wurden im vergangenen Jahr mit automatisch hochgezählten Versionsnummern veröffentlicht. Einige bemerkenswerte Punkte:
Das npm-Paket
isaacs-cliuikönnte ein Typosquatting-Versuch sein, der auf Isaacs eigenen Fork des Projektscliuiund das legitime npm-Paket in seinem Namespace abzielt: @isaacs/cliui.Das npm-Paket
azure-sdk-for-netkönnte Teil einer Dependency-Confusion-Kampagne sein, die private Pakete mit demselben Namen angreifen soll.Das npm-Paket
link-deepbeansprucht einen beliebten Funktionsnamen, der mit Utility-Paketen wie lodash und anderen verbunden ist.

Außerdem ist auf der npmjs-Profilseite des Nutzers himanshutester002 keine identifizierbare Information zu finden.
Wie bereits erwähnt, wird das npm-Paket strip-ansi-cjs von über 500 anderen Paketen verwendet – was zunächst auf Beliebtheit hindeuten könnte. Sehen wir sie uns an:

Die Aufnahme in die Liste könnte zunächst glaubwürdig wirken, aber stimmt das wirklich?
Namen wie clazz-transformer, react-native-multiply oder vielleicht gh-monoproject-cli wirken legitim. Sind sie es auch?
Hier sehen Sie die npm-Paket-Seite von react-native-multiply:

Dieses Paket hat praktisch keine Downloads und sein Autor ist ein anonymer npm-Nutzer ohne identifizierbare Angaben. Die Quellcode-Repository-URL, auf die dieses Paket verweist, existiert nicht: https://github[.]com/hasandader/react-native-multiply. Auch das GitHub-Nutzerprofil wirkt äußerst verdächtig und weist kaum nennenswerte Aktivitäten auf.
Das npm-Paket scheint zwar Quellcode zu enthalten, doch bei näherer Betrachtung zeigt sich, dass es sich um ein generiertes Codebeispiel für einen „Hello World“-Anwendungsprototyp handelt.

Man fragt sich auch: Wenn dieses Paket nur eine Multiplikationsbibliothek ist, warum benötigt es dann 776 Abhängigkeiten, um Folgendes zu tun?
Manche scherzen, dass JavaScript durch übermäßige Abhängigkeiten zu einem astronomisch großen Baum verschachtelter Pakete beiträgt. Ein Projekt mit 776 direkten Abhängigkeiten ist jedoch unverhältnismäßig groß.
Unter all diesen Abhängigkeiten finden sich auch die drei verdächtigen npm-Pakete, mit denen unsere Geschichte begann: string-width-cjs, strip-ansi-cjs und wrap-ansi-cjs:

Wir haben erwähnt, dass eine der Abhängigkeiten von strip-ansi-cjs den Namen clazz-transformer trägt. Sehen wir uns das Paket an:

Sehen wir uns genauer an, was hier vor sich geht. Das npm-Paket clazz-transformer trägt auf seiner README-Seite absichtlich den falschen Namen class-transformer. Außerdem passt sein Quellcode-Repository unter https://github[.]com/typestack/class-transformer nicht zum Paketnamen, was Zweifel an seiner Legitimität weckt.
Die Datei package.json im zugehörigen GitHub-Repository typstack/class-transformer sieht folgendermaßen aus:

Die Datei package.json auf GitHub enthält keine Angaben zu Abhängigkeiten. Sehen wir uns jedoch den Quellcode des tatsächlichen Pakets auf npmjs an, finden wir die 437 Abhängigkeiten, mit denen dieses clazz-transformer-Paket gebündelt ist. Darunter befinden sich praktischerweise wieder die drei verdächtigen *-cjs-Pakete:

Weitere Überlegungen zu den verdächtigen npm-Paketen
Bevor wir weitere Schlüsse ziehen, sollten wir einige Merkmale der oben genannten npm-Pakete erwähnen:
Die React-Native-Pakete scheinen mit dem Gerüst-Tool
create-react-native-libraryerstellt worden zu sein. Dieses Tool enthält auch die Standardbeispielfunktionmultiplyim Quellcode, der für ein neues Projekt generiert wird.Die Verzeichnis- und Dateistrukturen sowie Abhängigkeiten einiger Pakete könnten aus einem Next.js-14-Starter-Boilerplate stammen, etwa aus einem Projekt, das mit
npx create-next-app@14erstellt wurde.
Unsere Kolleginnen und Kollegen bei Sonatype haben zuvor ähnliche Fälle entdeckt, in denen Open-Source-Registries mit Paketen überflutet wurden. In diesen Fällen bestand das eigentliche Ziel darin, Entwickler mit Tea-Tokens zu belohnen. Tea ist eine Web3-Plattform zur Monetarisierung von Open-Source-Software.
Dass in den genannten Paketen tea.yaml-Dateien gefunden wurden, stützt zusätzlich die Vermutung, dass ein Teil des Ziels dieser Kampagne darin besteht, durch den Missbrauch von Tea Tea-Tokens zu schürfen.
Am 14. April 2024 veröffentlichte ein Nutzer des Tea-Forums einen Kommentar, der die Bedenken hinsichtlich des Missbrauchs von Tea weiter untermauert:

Bevor ich abschließend auf meine Gedanken eingehe, möchte ich Sébastien Lorber herzlich für seine umsichtige Haltung als Maintainer danken. Dank ihm konnten diese Hinweise auf einen potenziellen npm-Supply-Chain-Angriff ans Licht gebracht werden.
Was ist mit string-width-cjs los?
Inzwischen bin ich mir ziemlich sicher, dass ich auch bei den übrigen Paketen, die angeblich von string-width-cjs abhängen, weiter nachforschen und höchst zweifelhafte Hinweise auf deren tatsächliche Legitimität finden könnte.
Ich vermute, dass all diese abhängigen Pakete und die Download-Steigerungen einzig dazu dienen, den drei *-cjs-Paketen einen falschen Anschein von Legitimität zu verleihen. Zum richtigen Zeitpunkt könnten diese gefälschten Pakete dann bei einem geeigneten Opfer installiert werden – gefolgt von einer neuen, bösartigen Version.
Damit Sie bei der Arbeit mit Open-Source-Software geschützt bleiben, empfehle ich Ihnen dringend, Sicherheitspraktiken einzuführen und insbesondere die folgenden Lernressourcen zu nutzen:
Haben wir eine Supply-Chain-Angriffskampagne aufgedeckt, die hinter ihrem betrügerischen Vorgehen steckt, oder geht es letztlich nur um Geld und damit um Spam und den Missbrauch öffentlicher Registries wie npm und GitHub, um Tea-Tokens zu schürfen?
Wie sich die Lage auch entwickelt: Bleiben Sie wachsam.
Sichern Sie Ihren Code während der Entwicklung
Snyk scannt Ihren Code auf Qualitäts- und Sicherheitsprobleme und gibt Ihnen direkt in Ihrer IDE Empfehlungen zur Behebung.
