Artikel

Lockfiles sind keine Supply-Chain-Security-Strategie

Aktuelle npm-Kompromittierungen zeigen, warum JavaScript-Supply-Chain-Security CI-Isolation, Maintainer-Tokens, Install-Skripte, Provenance, Dependency-Policy und Incident Response abdecken muss.

#npm#typescript#ci-cd#supply-chain-security#devops

Viele JavaScript-Teams haben ein Lockfile und nehmen an, dass ihre Dependency-Installs damit kontrolliert sind.

Das ist ein guter Anfang. Es ist keine Strategie.

Ein Lockfile hilft, den Dependency-Graph zu reproduzieren, den man angefordert hat. Es beweist nicht, dass ein Paket sicher veröffentlicht wurde. Es schützt keinen CI-Runner, der ein bösartiges postinstall-Skript ausführt. Es verhindert nicht, dass ein kompromittierter Maintainer-Account eine vergiftete Version veröffentlicht. Es rotiert keine Cloud-Credentials, nachdem eine Build-Maschine fremden Code ausgeführt hat.

Aktuelle npm-Incidents zeigen immer wieder denselben Punkt: Der Dependency Tree ist Teil der Produktionsinfrastruktur. Ihn wie eine passive Sammlung von Libraries zu behandeln, ist nicht mehr realistisch.

Was aktuelle Incidents zeigen

Der TanStack-npm-Vorfall im Mai 2026 ist interessant, weil es nicht einfach "jemand hat ein npm-Token gestohlen" war. Laut offiziellem TanStack-Postmortem konnte der Angreifer bösartige Versionen über viele @tanstack/*-Pakete veröffentlichen, indem ein riskantes pull_request_target-Workflow-Muster, GitHub-Actions-Cache-Poisoning über eine Trust Boundary und ein OIDC-Token aus einem Runner-Prozess kombiniert wurden. TanStack sagt, dass keine npm-Tokens gestohlen wurden und der npm-Publish-Workflow selbst nicht kompromittiert war.

Dieses Detail ist wichtig. Viele Teams denken noch, Supply Chain Security bedeute "2FA für npm einschalten". Das ist notwendig, aber nicht ausreichend. Moderne Publish-Flows laufen über GitHub Actions, Repository-Rechte, Workflow-Trigger, Caches, OIDC, Package Registry Trust und Install-Time-Verhalten.

Der Axios-Incident zeigt ein anderes Muster. Datadog Security Labs analysierte bösartige Axios-Versionen, die eine versteckte Dependency plain-crypto-js nachzogen. Diese lief während der Installation und lieferte einen plattformübergreifenden Remote Access Trojan aus.

Das betroffene Paket musste nicht einmal vom Anwendungscode importiert werden. Die Installation reichte.

Genau das unterschätzen viele Teams: In npm kann Installation Ausführung bedeuten.

Was Lockfiles wirklich leisten

Lockfiles bleiben wertvoll. Sie machen Installationen reproduzierbar, reduzieren überraschende Upgrades, geben Reviewern konkrete Diffs und helfen bei Incident Response, weil man genaue Versionen kennt.

Aber Lockfiles arbeiten auf einer bestimmten Ebene. Sie sagen: "Löse diesen Graph mit diesem Package-Manager-Verhalten auf diese Versionen und Integrity Hashes auf."

Sie beantworten nicht:

  • War der Maintainer kompromittiert?
  • Wurde das Release durch den erwarteten CI-Workflow gebaut?
  • Führt das Paket Code während der Installation aus?
  • Lief die Installation mit Cloud-Credentials in der Umgebung?
  • Enthielt der CI-Cache vergiftete Artefakte?
  • Hat eine Dependency Secrets exfiltriert, bevor der Lockfile-Diff reviewed wurde?
  • Hat das Team schnell genug reagiert?

Ein Lockfile ist ein Reproduzierbarkeits-Control. Supply Chain Security braucht zusätzlich Execution-, Identity-, Network- und Incident-Controls.

Der gefährliche Moment ist die Installation

Das JavaScript-Ökosystem macht Installationen mächtig. Das ist praktisch, wenn native Module kompiliert oder Pakete vorbereitet werden müssen. Es ist gefährlich, wenn eine Dependency beliebige Lifecycle-Skripte auf einem Entwicklerlaptop oder CI-Runner ausführen kann.

Die unbequeme Frage lautet nicht: "Vertrauen wir React, Axios, TanStack oder einem anderen populären Projekt?" Die unbequeme Frage lautet: "Was kann irgendein Paket in unserem Dependency Graph tun, wenn es in unserer Umgebung installiert wird?"

Wenn npm install oder pnpm install mit breitem Netzwerkzugang, Repository-Tokens, Cloud-Credentials, Deployment Keys, Publish Tokens oder gemounteten Secrets läuft, braucht ein bösartiges Install-Skript keine Application Vulnerability. Es hat bereits eine nützliche Ausführungsumgebung.

Gute Teams reduzieren den Wert dieser Umgebung.

Ein praktisches Verteidigungsmodell

Kein Team kann jedes Package-Update manuell vollständig prüfen. Ziel ist nicht perfekte Prävention. Ziel sind Schichten, die Blast Radius reduzieren und Reaktion beschleunigen.

Ein pragmatisches npm-Supply-Chain-Modell umfasst:

1. Dependency-Change-Policy

Dependency-Updates sollten nicht in Feature-Arbeit verschwinden. Wo möglich getrennte Dependency-PRs nutzen. Install-Skripte, neue transitive Dependencies und Maintainer-Änderungen für wichtige Pakete prüfen.

2. Install-Isolation

Dependency-Installation in einer Umgebung mit minimalen Secrets ausführen. Wenn der Install-Step keine Cloud-Credentials, Deployment Keys, npm-Publish-Tokens, SSH-Keys oder Produktionsvariablen braucht, sollten sie nicht vorhanden sein.

3. Script-Kontrolle

Lifecycle-Skripte dort standardmäßig deaktivieren, wo es realistisch ist, und notwendige Pakete explizit erlauben. Das ist nicht kostenlos, aber allein zu wissen, welche Pakete Skripte brauchen, ist schon eine Verbesserung.

4. CI-Trust-Boundaries

Pull-Request-Workflows, Forks, Caches und Release-Workflows als getrennte Trust Zones behandeln. Besonders vorsichtig mit pull_request_target, geteilten Caches und Workflows mit Write Permissions oder OIDC-Credentials.

5. Token-Hygiene

Langlaufende npm-Tokens vermeiden, wo Trusted Publishing oder OIDC-basierte Flows sinnvoll verfügbar sind. Publish-Credentials aus normalen Build-Jobs heraushalten.

6. Provenance und Artefakt-Disziplin

Pakete und interne Artefakte mit Provenance-Metadaten bevorzugen, wo möglich. Build Outputs nachvollziehbar an Commits und Workflows binden.

7. Monitoring und Response

Wissen, wo Dependency-Installs passieren: lokale Maschinen, CI, Docker Builds, Preview-Umgebungen, Deployment-Jobs und Scheduled Automation. Bei einem kompromittierten Paket muss die Frage "Wo lief das?" schnell beantwortbar sein.

Was ich in CI/CD-Reviews prüfe

Bei JavaScript- oder TypeScript-Pipelines interessiert mich weniger, ob ein modisches Tool eingesetzt wird. Mich interessiert, ob das Trust-Modell kohärent ist.

Typische Fragen:

  • Kann ein untrusted Pull Request einen trusted Release Job beeinflussen?
  • Werden Install-Caches über Trust Boundaries geteilt?
  • Haben Install- oder Test-Jobs Zugriff auf Secrets, die sie nicht brauchen?
  • Sind npm-Publish-Credentials langlaufend?
  • Können Maintainer manuell von Laptops publishen?
  • Werden Dependency-Updates getrennt von Feature-Code reviewed?
  • Gibt es einen Prozess, Dependency-Updates während eines aktiven Incidents einzufrieren?
  • Kann das Team CI-Logs, Lockfiles, Container-Build-Logs und Deployment-Historie schnell nach einer kompromittierten Version durchsuchen?

Die Antwort muss nicht "alles ist perfekt abgesichert" sein. Sie muss zeigen, dass das Team versteht, wo Code ausgeführt wird und welche Secrets von dort erreichbar sind.

Incident Response beginnt vor dem Incident

Wenn ein populäres Paket kompromittiert wird, ist die erste Stunde chaotisch. Informationen ändern sich, Versionen werden deprecatet, Registry-Verhalten hängt hinter Advisories, Security-Anbieter veröffentlichen Indicators.

Während dieser Stunde will man den Prozess nicht erfinden.

Mindestens sollte ein Team können:

  • Alle Repositories mit betroffenem Paket finden.
  • Exakte installierte Versionen aus Lockfiles und CI-Logs bestimmen.
  • Klären, ob die bösartige Version auf Entwicklergeräten, CI-Runnern oder Produktions-Build-Umgebungen lief.
  • Secrets rotieren, abhängig davon, wo die Installation lief.
  • Betroffene Caches leeren oder invalidieren.
  • Bekannte schlechte Versionen in Package-Manager- oder Registry-Proxy-Policy blockieren.
  • Artefakte aus sauberen Umgebungen neu bauen.
  • Die Timeline dokumentieren und daraus eine Dependency-Policy-Änderung ableiten.

Hier überschneiden sich Platform Engineering und Security. Es geht nicht nur um "Dependencies scannen". Es geht darum, das Delivery-System verständlich genug zu machen, damit das Team reagieren kann, wenn Scanner noch nicht aufgeholt haben.

Das bessere mentale Modell

Eine Dependency ist nicht nur Source Code. Sie ist ein veröffentlichtes Artefakt aus Maintainer-Accounts, Repositories, CI-Workflows, Registry-Identität, Release-Automation, Install-Skripten und transitiven Dependencies.

Das Lockfile erfasst einen Ausschnitt dieses Systems. Nicht das ganze System.

Das bessere mentale Modell:

Jede Dependency-Installation ist untrusted code execution, bis das Gegenteil bewiesen ist.

Das bedeutet nicht, dass Entwicklung langsam oder paranoid werden muss. Es bedeutet, dass die Build-Pipeline so entworfen sein sollte, dass ein bösartiges Paket möglichst wenig stehlen, verändern oder veröffentlichen kann.

Wenn Ihr Team Node.js, TypeScript, Next.js oder React ausliefert und nie geprüft hat, welche Secrets während Dependency-Installation verfügbar sind, ist das ein guter Startpunkt.

Sprechen Sie mit mir direkt über Ihr Projekt

Projektgespräch buchen