Was ist ein Supply-Chain-Angriff?
Bei einem Supply-Chain-Angriff („Zuliefer-Ketten-Angriff“) werden ein oder mehrere Software-Projekte kompromittiert, indem ein von ihnen genutztes Software-Paket, eine „Abhängigkeit“, kompromittiert wird. Der kompromittierte Code ist nicht unter der Kontrolle der Projektmitarbeiter. Heutige Softwareprojekte haben oft viele Abhängigkeiten und diese werden mit verschiedenen Package-Manager-Tools („Paketverwaltungs-Werkzeugen“) verwaltet und installiert.
Was ist das spezifische Problem?
Supply-Chain-Angriffe werden immer häufiger. Das letzte große Beispiel ist die Kompromittierung des axios npm-Pakets. npm (Node Package Manager) ist eine Paketverwaltung für JavaScript Code. Egal wie die jeweiligen Software-Pakete kompromittiert wurden, taucht ein Angriffsvektor immer wieder auf: "postintall"-Scripte. Diese Scripte, auch Hooks genannt, sind zum Ausführen von weiteren Installationsschritten nach dem Herunterladen von Paketen gedacht und sind npm-spezifisch. Andere Package-Manager besitzen allerdings ähnliche Mechanismen, die auch schon als Angriffsvektor missbraucht wurden. Theoretisch können auch andere Hooks als Angriffsvektor missbraucht werden, nicht nur der "postinstall" Hook. Der kritische Punkt, warum diese Angriffe so erfolgreich sind, liegt allerdings darin, dass diese Scripte ohne Nutzerinteraktion ausgeführt werden. Noch kritischer wird es dadurch, dass nicht nur direkte Abhängigkeiten, sondern auch indirekte, transitive Abhängigkeiten Scripte ausführen können. npm hat zwar eine --ignore-scripts Option, allerdings deaktiviert diese sämtliche Scripte, von allen Paketen. Man hat damit keine Möglichkeit nur einzelne Scripte laufen zu lassen. Es gibt eine seit 2019 laufende RFC-Diskussion und einen offenen Pull-Request, selektives Ausführen von Skripten in npm zu ermöglichen, allerdings sind diese scheinbar gegen Ende 2021 eingeschlafen.
Eine Lösung: pnpm
Das ist einer der Gründe warum ich mittlerweile empfehle pnpm anstatt npm zu benutzen. pnpm fragt den Nutzer vor dem Ausführen von Scripten um Erlaubnis und man kann sich einzeln per Script entscheiden, ob man es ausführen will oder nicht. Dieser einfache Mechanismus macht den Angriffsvektor Scripte nicht komplett unmöglich, aber man hat mit diesem Mechanismus als Benutzer zumindest eine Verteidigungslinie.
Weitere Vorteile von pnpm
Darüber hinaus bietet pnpm noch weitere Vorteile, wie zum Beispiel die bessere Nutzung von Speicherplatz durch Abhängigkeiten. npm installiert sämtliche Abhängigkeiten für jedes Projekt erneut im node_modules Ordner des Projektes. Dadurch liegen Duplikate der selben Abhängigkeit in jedem Projekt auf dem System. Außerdem wird jede Version einer Abhängigkeit komplett auf dem System gespeichert, selbst wenn sich nur eine Datei unterscheidet. Dadurch kann es sehr schnell passieren, dass in einem npm Projekt der node_modules Ordner von mehreren hundert Megabyte bis zu mehreren Gigabyte wachsen kann. pnpm vermeidet diese Duplikate und speichert sämtliche Pakete an einem zentralen Platz im System, auch über Projekte hinweg. Zusätzlich werden für unterschiedliche Versionen des selben Pakets nur die unterschiedlichen Dateien gespeichert. Gleiche Dateien werden wiederverwendet. Dadurch reduziert pnpm die Größe des node_modules Ordners problemlos auf unter einhundert Megabyte. Außerdem wird dadurch verhindert, die gleichen Abhängigkeiten für neue Projekte immer wieder herunter laden zu müssen.
Umstellung von npm auf pnpm
Für die meisten Projekte sollte es ohne große Umstellung möglich sein, von npm auf pnpm umzustellen:
- Zuerst muss
pnpminstalliert werden. Am einfachst geht dies mitnpmselbst:npm install --global pnpm
Alternativen kann man inpnpm’s Dokumentation finden. - Als nächstes muss man den
node_modulesOrdner des Projektes löschen, dass man umstellen will. - (Optional) Man kann mit Hilfe des Paketes
only-allowerzwingen, dass nurpnpmals Package-Manager zulässig ist. Dazu muss man folgendes zurpackage.jsonDatei des Projektes hinzufügen:"scripts": { "preinstall": "npx only-allow pnpm" } - Anschließend muss man
pnpmdie aktuelle Konfiguration der Abhängigkeiten importieren lassen:pnpm import - Danach muss man die
package-lock.jsonDatei löschen. - Zuletzt lässt man
pnpmdie Abhängigkeiten wieder installieren und dennode_modulesneu generieren:pnpm install
Danach sollten die meisten npm Befehle in pnpm ähnlich funktionieren, man muss nur pnpm anstatt npm als Werkzeug benutzen. Für mehr Information dient die pnpm Dokumentation.
Es gibt einzelne Dinge, die unter pnpm anders funktionieren, diese sind aber eher selten und führen hier zu weit. Für weitere Informationen um Supply-Chain-Angriffe mit pnpm zu verhindern verweise ich auf diesen Eintrag in pnpm’s Dokumentation.







