Warum npm-Lockfiles ein Sicherheitsrisiko beim Einschleusen schädlicher Module darstellen können
24. September 2019
0 Min. LesezeitKürzlich habe ich begonnen, mich mit dem Gedanken zu beschäftigen, Pakete im npm-Ökosystem auf Bedrohungen zu untersuchen. Kann sich ein event-stream-Vorfall wiederholen? Wie sieht es mit anderen Supply-Chain-Angriffen aus? Was wird der nächste Angriffsvektor sein, den wir noch nicht kennen – und ließe er sich vielleicht vollständig verhindern?
Und dann hatte ich eines Tages einen Geistesblitz! Lassen Sie mich zeigen, wie leicht sich Hintertüren einschleusen lassen, die Projektverantwortlichen oft entgehen – und Ihren Code dadurch unsicher machen.
Das folgende Bild zeigt, wie ich über einen Pull Request, den ich für ein Open-Source-Paket auf npm erstellt habe, ein schädliches Paket eingeschleust habe.
Sehen Sie genau hin und prüfen Sie, ob Sie es entdecken:

Wenn ich nun meine Checkliste für das Review dieses Pull Requests durchgehe, sieht sie so aus:
Die von mir hinzugefügten Pakete sind im Ökosystem bekannt und frei von Sicherheitslücken – geprüft.
Die Paketnamen enthalten keine Typosquatting-Versuche – geprüft.
Es handelt sich um gültige Versionen dieser Pakete, die an sich nicht schädlich sind – geprüft.
Sieht gut aus. Dann können wir den Pull Request zusammenführen!
Moment mal. Haben Sie dieses Lockfile sorgfältig geprüft? Natürlich nicht – und das kann ich Ihnen nicht verdenken. Angesichts der folgenden Einschränkungen ist das auch alles andere als offensichtlich:
Die GitHub-Oberfläche klappt Diff-Abschnitte standardmäßig ein, wenn sie mehr als ein paar Hundert Codezeilen umfassen.
Wozu diese Datei überhaupt prüfen? Sie wird maschinell generiert und ist zudem nicht besonders gut lesbar.
Aber was könnte trotz dieser Einschränkungen schon schiefgehen?
Klappen wir den Diff auf und sehen uns den Inhalt an:

Wenn ein Lockfile vorhanden ist – sei es Yarns yarn.lock oder npms package-lock.json –, dient es bei der Installation als maßgebliche Quelle für Paketversionen und Paketquellen, aus denen Abhängigkeiten installiert werden.
Wissen Sie noch, was ich vorhin meinte? Eine Änderung von 500 Zeilen in einem Lockfile ist eigentlich ziemlich gering. Ich habe schon Tausende geänderte Zeilen in einer npm-package-lock.json- oder einer Yarn-Datei yarn.lock gesehen. Ich meine: Wer prüft wirklich Tausende Zeilen mit Zeichenänderungen? Erschwerend kommt hinzu, dass wir in diesem Lockfile Änderungen an Abhängigkeitsversionen und Metadatenaktualisierungen sehen – insgesamt nichts Ungewöhnliches.
Wenn wir weiter nach unten scrollen und die übrigen Änderungen in dieser Datei prüfen, stoßen wir auf dieses Schmuckstück:

Bei genauerem Hinsehen wird klar: Ich habe das ursprüngliche npm-Paket ms so geändert, dass stattdessen meine eigene Version verwendet wird, die in meinem GitHub-Repository liegt. Ich hätte es aus der offiziellen npm-Registry beziehen müssen, wie es ursprünglich im Lockfile des Projekts festgelegt war.
Dieses Projekt verwendet mehrere Versionen von ms, aber ich habe nur ms@2.1.1 so geändert, dass meine eigene angepasste Variante zum Einsatz kommt:
Es ist ziemlich schwierig, daran zu denken, Änderungen auf dieser Detailstufe zu prüfen – kein Wunder, dass so wenige Lockfile-Änderungen tatsächlich Zeichen für Zeichen überprüft werden!
Was macht also meine eigene Version von ms@2.1.1?
Sobald dieser Pull Request zusammengeführt wird, schleuse ich meine schädliche Version von ms@2.1.1 in den Code ein, um ihr Verhalten zur Laufzeit zu kontrollieren.
Auf diese Weise könnte ich eine Hintertür einbauen, die Logik des ms-Moduls verändern oder Postinstall-Skripte ausführen. Außerdem ist die Hosting-URL unter https://github.com/lirantal/ms/tarball/master für dieses Experiment natürlich verdächtig. Aber würde es Ihnen leichtfallen, das zu erkennen, wenn ich eine Typosquatting-Domain kaufen und das Paket unter https://registry.npmgs.org/ms/ hosten würde?
Für dieses Experiment enthält mein auf ms gehostetes Paket ein einfaches Postinstall-Skript, das Text ausgibt und in eine Datei schreibt. So können Sie nach der Installation des Pakets überprüfen, ob das Postinstall-Ereignis einfach bei einem yarn install ausgeführt wird:
Bei projektbasierten Anwendungen ist die Sicherheit von Lockfiles entscheidend. Sie sind anfällig für die in diesem Artikel beschriebenen Angriffe durch das Einschleusen schädlicher Inhalte.
Lockfiles in Paketen, die als Bibliotheken verwendet werden, sind dagegen ausschließlich für den Entwicklungsworkflow des jeweiligen Pakets gedacht. Wird das Projekt (Paket) als Abhängigkeit in einem anderen Projekt installiert, kommt sein Lockfile daher nicht zum Einsatz. Das bedeutet, dass das Einschleusen in Lockfiles bei Bibliotheken vor allem Projektverantwortliche und Mitwirkende gefährdet.
Wie können wir die Sicherheit von Lockfiles verbessern?
Da Lockfiles für die maschinelle Verarbeitung gedacht sind, lassen sie sich leicht automatisch prüfen. Damit eignen sie sich perfekt für einen Linter und vordefinierte Richtlinien!
Als Gegenmaßnahme gegen den oben beschriebenen Angriffsvektor habe ich lockfile-lint entwickelt. Ich habe die Prüfung als zusätzlichen, kurzen Schritt in die statische Codeanalyse integriert, die Sie auf einem CI-Server wie Travis CI oder Circle CI ausführen können.
Mindestens sollten Sie alle Ressourcen validieren, die bereitgestellt werden:
über HTTPS
aus einer vertrauenswürdigen Quelle
Diese Prüfungen in der CI für ein Yarn-Projekt auszuführen, ist zum Beispiel ganz einfach:
Hier sehen Sie ein Beispiel für häufige Fehler, die beim Linting des Lockfiles eines Projekts erkannt werden:

Zusammenfassung
Angriffe können viele verschiedene Formen annehmen – sogar über Lockfiles!
Mit dem event-stream-Vorfall haben wir den ersten erfolgreichen Social-Engineering-Angriff auf Maintainer von Open-Source-Projekten erlebt. Bislang war dies vielleicht einer der ausgeklügeltsten Angriffe auf die Software-Supply-Chain-Sicherheit im npm- und JavaScript-Ökosystem.
Ein Lockfile-Angriff könnte ähnlich ablaufen. Deshalb sollten Sie geeignete Maßnahmen ergreifen, um dieses Problem einzudämmen oder zumindest das Risiko zu verringern. Beachten Sie Folgendes:
Prüfen Sie Änderungen an Lockfiles sorgfältig (lockfile-lint kann Ihnen dabei helfen).
Lassen Sie Änderungen an Lockfiles nur von Maintainerinnen und Maintainer des Kernteams vornehmen, nicht von gelegentlichen Mitwirkenden.
Verzichten Sie bei Bibliotheken möglichst ganz auf Lockfiles.
Wenn Sie mehr über Lockfiles und ihre Verwendung erfahren möchten: Ich habe ausführlich darüber geschrieben, wie Sie package-Lockfiles im npm-Ökosystem verstehen.
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.


