Skip to main content

Das rätselhafte Supply-Chain-Risiko des npm-Pakets string-width-cjs

Artikel von
feature snyk supply chain purple

3. Oktober 2024

0 Min. Lesezeit

Diese 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:

npm-Paket-Aliase in einem Pull Request

Besonders fällt die Änderung an den npm-Abhängigkeiten auf, die eine ungewohnte Syntax verwendet:

  "dependencies": {
    "string-width": "^5.1.2",
    "string-width-cjs": "npm:string-width@^4.2.0",
    "strip-ansi": "^7.0.1",
    "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
    "wrap-ansi": "^8.1.0",
    "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"

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:

npx lockfile-lint --path package-lock.json --allowed-hosts yarn npm --validate-https --validate-package-names

detected resolved URL for package with a different name: string-width-cjs
    expected: string-width-cjs
    actual: string-width

detected resolved URL for package with a different name: strip-ansi-cjs
    expected: strip-ansi-cjs
    actual: strip-ansi

detected resolved URL for package with a different name: wrap-ansi-cjs
    expected: wrap-ansi-cjs
    actual: wrap-ansi

 ✖ Error: security issues detected!

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.

Verdächtiges npm-Paket strip-ansi-cjs

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?

Das Paket strip-ansi-cjs enthält keinen Quellcode

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-cliui könnte ein Typosquatting-Versuch sein, der auf Isaacs eigenen Fork des Projekts cliui und das legitime npm-Paket in seinem Namespace abzielt: @isaacs/cliui.

  • Das npm-Paket azure-sdk-for-net könnte Teil einer Dependency-Confusion-Kampagne sein, die private Pakete mit demselben Namen angreifen soll.

  • Das npm-Paket link-deep beansprucht einen beliebten Funktionsnamen, der mit Utility-Paketen wie lodash und anderen verbunden ist.

npm-Pakete, die einem verdächtigen Maintainer auf npm gehören

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:

Vom npmjs-Registry abhängige Pakete

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:

npm-Paket-Seite für react-native-multiply mit 776 Abhängigkeiten, Installationsbefehl, Verwendungscode und wöchentlichen Downloads.

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.

Dateibrowser mit dem Projektverzeichnis /react-native-multiply/ und den Ordnern Android, C++, iOS, library und source sowie Paketdateien.

Man fragt sich auch: Wenn dieses Paket nur eine Multiplikationsbibliothek ist, warum benötigt es dann 776 Abhängigkeiten, um Folgendes zu tun?

import { multiply } from 'react-native-multiply';
const result = await multiply(3, 7);

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:

Code-Auflistung mit der Version ^5.1.1 des npm-Pakets string-width-cjs neben verwandten String-Hilfsbibliotheken

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:

npm-Paket-Seite für class-transformer mit README, Installationsbefehl, Abhängigkeiten, Version, Lizenz und Download-Statistiken.

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:

Dunkler Code-Editor mit einem JSON-Paketmanifest für class-transformer, Version 0.5.1, mit Repository- und Moduldetails

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:

Ansicht des npm-Paketcodes mit einer JSON-Abhängigkeitsliste für clazz-transformer, einschließlich Paketnamen und Versionsbereichen

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-library erstellt worden zu sein. Dieses Tool enthält auch die Standardbeispielfunktion multiply im 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@14 erstellt 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:

Kommentar in einem Tea-Missbrauchsforum: Die meisten Projekte sind nutzloser Spam

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.