DevSecOpsJan Kahmen7 min Lesezeit

npm Supply Chain härten: Lehren aus Mini Shai-Hulud

Mini Shai-Hulud kompromittierte 42 TanStack-Pakete über GitHub Actions. Drei Hardening-Schichten gegen npm Supply-Chain-Angriffe in der Praxis.

In der Nacht zum 12. Mai 2026 publizierte das Angreiferkollektiv TeamPCP innerhalb weniger Stunden 84 manipulierte Versionen über 42 TanStack-Pakete im npm-Register. Betroffen waren neben TanStack auch Mistral AI und Guardrails AI auf PyPI, ergänzt um weitere npm-Pakete aus dem OpenSearch- und Squawk-Umfeld. Die Kampagne läuft unter dem Namen Mini Shai-Hulud und reiht sich in eine Serie ein, die seit Ende April 2026 im Wochenrhythmus DevSecOps-Toolchains trifft. Vier Tage zuvor hatte derselbe Akteur das offizielle Checkmarx Jenkins AST Plugin kompromittiert.

Was diesen Vorfall vom klassischen Maintainer-Account-Hijacking unterscheidet, ist der Angriffsweg. Die Angreifer haben keinen einzigen Maintainer überredet, ein bösartiges Paket zu publizieren. Sie haben den Build-Bot übernommen. Damit verschiebt sich die Verteidigung weg vom Vertrauen in einzelne Menschen und hin zur Härtung der Pipelines, die im Hintergrund publizieren. Dieser Artikel ordnet den Angriff technisch ein und beschreibt drei Hardening-Schichten, die TanStack-artige Vorfälle in Zukunft erkennbar oder unmöglich machen.

Anatomie des Angriffs

Der Vektor ist ein verketteter Workflow-Angriff über pull_request_target in GitHub Actions in Kombination mit Cache Poisoning. Der pull_request_target-Trigger ist eine der gefährlichsten Konfigurationen in GitHub Actions, weil er den Workflow im Kontext des Ziel-Branches mit Zugriff auf alle Repository-Secrets ausführt, während der Code aus dem Pull Request stammt. GitHub dokumentiert diese Konstellation explizit als Risiko in seinem Security Hardening Guide für GitHub Actions.

TeamPCP hat dieses Setup zweistufig ausgenutzt. Erst wurde ein präparierter Pull Request gegen das TanStack-Repository eingereicht, dessen Code beim Build den Aktions-Cache vergiftet hat. Anschließend lief ein vertrauenswürdiger Workflow im Kontext des main-Branches an, las den vergifteten Cache zurück und führte den injizierten Code mit den Repository-Secrets aus. Das Endergebnis war der Diebstahl des OIDC-Tokens direkt aus dem Runner-Prozess. Mit diesem Token konnte der Angreifer signierte npm-Publishes auslösen, die für Konsumenten von echten Releases nicht zu unterscheiden waren.

Die Payload trägt den Namen router_init.js und ist obfuskiert. Sie greift bei jeder Ausführung den AWS Instance Metadata Service, den GitHub-Token-Speicher des Runners, SSH-Schlüssel im Standardpfad sowie Credentials aus gängigen CI-Systemen ab und exfiltriert sie an die Domain filev2.getsession.org. Anschließend nutzt der Code die GitHub GraphQL API mit den gestohlenen Tokens, um in den Repositories der Opfer weitere Trust-Beziehungen zu setzen. Externe Forscher wie Aikido Security, Endor Labs, SafeDep und Socket erkannten die Welle nach etwa 20 Minuten. Die offiziellen Pipeline-Owner brauchten mehrere Stunden, bis die kompromittierten Versionen depubliziert waren.

Die zugehörige Schwachstelle wird als CVE-2026-45321 mit einem CVSS-Score von 9.6 geführt.

Sofortmaßnahmen für Betroffene

Wer eines der betroffenen Pakete im Lockfile hat, sollte heute drei Schritte parallel anstoßen. Der erste ist die Identifikation aller Build-Artefakte, die mit den kompromittierten Versionen erzeugt wurden. Container-Images, Lambda-Pakete und statische Bundles aus dem Zeitfenster zwischen dem 11. und 12. Mai 2026 müssen als potenziell verseucht behandelt werden, auch wenn der router_init.js-Code beim eigenen Build nicht offensichtlich gefeuert hat.

Der zweite Schritt ist die Rotation aller Geheimnisse, die der Runner-Prozess kannte. Die Reihenfolge ist nicht beliebig, sondern ergibt sich aus der Vertrauenskette: zuerst die npm-Tokens, weil sie unmittelbar Publish-Rechte vergeben, danach GitHub Personal Access Tokens und OIDC-Trusts, dann AWS Access Keys und Session Tokens, anschließend Vault-Tokens und Kubernetes Service Account Tokens. Wer die Reihenfolge umkehrt, riskiert, dass eine bereits exfiltrierte Identität die frisch rotierte ebenfalls mitnimmt.

Der dritte Schritt ist eine forensische Prüfung der CI-Logs der letzten sieben Tage. Ausgehende Verbindungen zu filev2.getsession.org, ungewöhnliche GitHub-GraphQL-Aufrufe und neue Einträge in den OIDC-Trust-Policies sind die wichtigsten Indikatoren. Wer keine zentralisierten Audit-Logs für GitHub Actions hat, sollte den Vorfall zum Anlass nehmen, das Logging in ein SIEM zu spiegeln.

Strukturelle Härtung gegen Pipeline-Übernahmen

Hardening fängt bei dem Trigger an, der den Angriff erst möglich gemacht hat. pull_request_target sollte aus jedem öffentlichen Repository entfernt werden, in dem es nicht zwingend benötigt wird. In den seltenen Fällen, in denen der Trigger nötig ist, etwa für das automatische Labeling von externen Beiträgen, gilt die Trennung in zwei Workflows: Der erste Workflow läuft mit dem PR-Code und ohne jeden Zugriff auf Repository-Secrets, der zweite Workflow läuft erst nach manueller Freigabe und mit den Secrets, aber ohne den PR-Code auszuchecken. Diese Trennung wird in den GitHub-Empfehlungen zu pull_request_target seit 2020 beschrieben, ist in der Praxis aber selten umgesetzt.

Cache Poisoning lässt sich durch klare Cache-Key-Strategien begrenzen. Wer einen Cache-Schlüssel über den Hash der package-lock.json aus dem PR-Code aufbaut, gibt einem Angreifer die Kontrolle über den Cache-Inhalt. Sicherer ist es, Cache-Keys ausschließlich an vertrauenswürdige Inputs zu binden, also etwa an den Commit-Hash des main-Branches oder an statische Versions-Tags. Workflows, die fremden Code bauen, sollten den Cache komplett deaktivieren oder einen separaten Cache-Namespace ohne Lese-Schreib-Beziehung zum Produktionsbau pflegen.

Bei der OIDC-Trust-Konfiguration ist die häufigste Schwäche eine zu großzügige Subject-Bedingung. Trust Policies wie repo:org/repo:* erlauben jedem Workflow im Repository, AWS- oder GCP-Tokens zu beziehen, also auch einem böswilligen Workflow aus einem Fork. Die Trust Policy sollte stattdessen auf konkrete Refs eingeschränkt sein, etwa repo:org/repo:ref:refs/heads/main oder auf bestimmte Reusable Workflows. Cloudflare hat das Vorgehen in einem öffentlichen Blogbeitrag dokumentiert, nachdem ein interner Audit dieselbe Schwäche zutage gefördert hatte.

Defense in Depth über die gesamte Lieferkette

Die strukturelle Härtung der eigenen Workflows ist notwendig, aber nicht hinreichend. Mini Shai-Hulud zeigt, dass Angreifer den Code, den ein Unternehmen ausführt, lange vor dessen npm install manipulieren können. Die folgende Schichtung erhöht den Aufwand für den Angreifer und verschafft den Verteidigern Zeit.

Auf der ersten Schicht steht das Vermeiden von Lifecycle-Skripten in der CI. npm ci --ignore-scripts verhindert, dass postinstall-Hooks beim Build feuern. Das bricht eine kleine Zahl von Paketen, deckt aber genau die Klasse von Angriffen ab, in der die Payload bei der Installation aktiv wird. Die zweite Schicht ist die strikte Pinning-Strategie: exakte Versionen statt ^- oder ~-Bereiche, regelmäßige Verifikation der Lockfile-Integrität, automatisierte Diffs gegen die letzte freigegebene Version.

Die dritte Schicht ist Pre-Publish-Scanning durch externe Dienste wie Socket, Aikido, Endor Labs oder Snyk. Diese Dienste analysieren neue Paketversionen in nahezu Echtzeit auf verdächtige Muster wie versteckte Netzwerk-Calls, Obfuskation oder ungewöhnliche Dateizugriffe und veröffentlichen Alerts, bevor die Pakete bei Endkunden ankommen. Genau diese Tools haben den TanStack-Hit in unter 20 Minuten erkannt. Sie ersetzen weder Pinning noch Audits, sind aber heute die schnellste verfügbare Frühwarnstufe.

Die vierte Schicht ist eine private Registry mit Quarantäne-Latenz. Verdaccio, JFrog Artifactory, Sonatype Nexus oder GitHub Packages lassen sich so konfigurieren, dass frisch publizierte Versionen erst nach 24 bis 48 Stunden in den internen Spiegel übernommen werden. Diese Latenz reicht in der Regel aus, um die genannten Frühwarndienste eine Welle erkennen zu lassen. Ergänzend dazu lässt sich SLSA Level 3 verlangen, sodass nur Artefakte mit reproduzierbarer Build-Provenance in den internen Mirror gelangen. npm provenance und Sigstore-Signaturen sind die offenen Standards, die diese Provenance abbilden.

Was nicht hilft

Drei verbreitete Annahmen werden durch Mini Shai-Hulud widerlegt. Erstens ist reines Maintainer-Vertrauen kein Schutzmodell mehr. Niemand bei TanStack hat absichtlich eine bösartige Version publiziert. Die Frage ist nicht, ob der Maintainer integer ist, sondern ob seine Pipeline integer ist.

Zweitens reicht npm audit nicht als Frühwarnsystem. Das Tool meldet bekannte Schwachstellen aus Advisory-Datenbanken, die typischerweise mit einer Verzögerung von Stunden bis Tagen befüllt werden. Die kritische Phase eines Supply-Chain-Vorfalls liegt in den ersten Minuten nach der Publikation.

Drittens ist automatischer Patch-Merge durch Dependabot oder Renovate ohne Verzögerung gefährlich. Wer auf patch-Updates automerged, ohne ein Quarantäne-Fenster oder mindestens einen externen Scan vorzuschalten, integriert eine kompromittierte Version genau in dem Zeitfenster, in dem die Detection läuft. BSI-Empfehlungen zur Software-Lieferkette und das NIST Secure Software Development Framework decken diese Anforderung mittlerweile explizit ab.

Fazit

Mini Shai-Hulud ist kein isolierter Vorfall, sondern eine Serie, die die Toolchain-Klasse als Angriffsoberfläche etabliert hat. Verteidiger, die nur auf bekannte Maintainer und auf npm audit setzen, schauen auf einen Angriffsweg, den TeamPCP gerade verlassen hat. Wer hingegen pull_request_target-Trigger entschärft, OIDC-Trusts eng schneidet, eine private Registry mit Quarantäne-Latenz fährt und Pre-Publish-Scanning konsequent in den Build einbindet, reduziert die Angriffsfläche auf ein verteidigbares Maß. Wir prüfen diese vier Punkte als festen Bestandteil unserer DevSecOps-Pentests und sehen in den meisten Engagements mindestens zwei davon offen. Genau dort liegt 2026 die Hebelwirkung.

Unsere Services