Rusts Lieferkette unter Beschuss: Warum ein Angriff nur der Anfang war

17 Prozent Blindflug: Was crates.io über Software-Sicherheit verrät
Abstract
- #Rust
- #crates.io
- #Lieferkette
- #Supply Chain
- #Backdoor
- #Sicherheit
- #Softwareentwicklung
Von Go lernen: Wie Rust seine Abhängigkeiten sicherer machen kann
Stellen Sie sich einen Supermarkt vor, in dem alle Lieferanten der Region ihre Ware abgeben. Alles läuft über eine einzige Rampe. Wer dort eine manipulierte Packung dazwischenschiebt, erreicht mit einem Handgriff jeden Kunden der Stadt. Genau so funktioniert eine zentrale Paketregistry und genau das ist im August 2026 im Rust-Ökosystem passiert.
Ein Angriff, der glimpflich ausging
Betroffen waren Pakete, die mehr als eine Million Mal pro Tag heruntergeladen werden. Neun Pakete mit Hintertür standen zur Verfügung, bis das Rust Security Response Team sie nach rund 110 Minuten entfernte. Das ist eine bemerkenswert schnelle Reaktion und verdient Anerkennung.
Die unangenehme Wahrheit lautet trotzdem: Der Vorfall war weitgehend vermeidbar, und es gibt nichts, was eine Wiederholung verhindern würde. Mit hoher Wahrscheinlichkeit liegen weitere bösartige Pakete auf crates.io, die schlicht noch niemand entdeckt hat.
Nähern wir uns dem Thema wie Sherlock Holmes: Wenn man alle sehr schlechten Ideen ausgeschlossen hat, muss das, was übrig bleibt, die am wenigsten schlechte Idee sein.
Warum das Problem tiefer liegt
Rust hat sich bei der Verwaltung von Abhängigkeiten für denselben Weg entschieden wie JavaScript: eine kleine Standardbibliothek plus eine zentrale Registry, die man für alles jenseits von "Hallo Welt" zwingend braucht.
Die zentrale Registry als Nadelöhr
Für Angreifer ist eine solche Registry ein Geschenk. Sie schafft einen einzigen Ausfallpunkt und eine Schattenzone zwischen dem Autor eines Pakets und dessen Nutzern, ein idealer Ort, um Code zu verstecken.
17 Prozent, die niemand kennt
Adam Harvey hat die 999 beliebtesten Crates untersucht. Bei etwa 17 Prozent stimmte der veröffentlichte Code nicht mit dem zugehörigen Code-Repository überein.
Man muss sich das auf der Zunge zergehen lassen: Bei jedem sechsten der populärsten Rust-Pakete enthält der ausgelieferte Inhalt Code, von dem praktisch niemand weiß, was er tut. Was in der langen, weniger beachteten Masse an Paketen schlummert, mag man sich kaum ausmalen.
Eine schmale Standardbibliothek und ihre Folgen
Eine magere Standardbibliothek führt zwangsläufig zu einer Explosion transitiver Abhängigkeiten. Mit jeder Ebene wächst nicht nur die Zahl der Pakete, sondern auch die Zahl der Autoren, denen Sie vertrauen müssen, um ein simples Programm auszuliefern.
Die lange Liste der Angriffswege
Zentrale Paketregistries eröffnen so viele Angriffsvektoren, dass man leicht den Überblick verliert. Veröffentlichungs-Tokens lassen sich stehlen und hochansteckende Würmer sind zurück. Der veröffentlichte Code muss nicht dem Quell-Repository entsprechen. Datenbank oder Speicherschicht der Registry können kompromittiert werden. Zwischen Veröffentlicher und Registry ist ein Man-in-the-Middle möglich. Konten lassen sich übernehmen. Dazu kommen Typosquatting, Namesquatting und Dependency Confusion. Und das ist nur die Liste, die einem spontan einfällt.
Drei Reifegrade der Lieferketten-Sicherheit
Stufe -2: Was ich nicht sehe, gibt es nicht
Digitale Systeme sind so komplex, dass selbst fortgeschrittene Anwender kaum ein Prozent davon verstehen. Prozessoren, Compiler, Betriebssysteme, für die meisten ist das dunkle Magie.
Das ist zunächst nicht schlimm, sonst käme niemand zum Arbeiten. Die Kehrseite ist jedoch eine Denkfigur, die sich einschleicht: "Ich verstehe es nicht, also schaue ich nicht hin, also sehe ich es nicht, also existiert es nicht." Hier stehen die meisten Organisationen heute und deshalb sind Angriffe auf die Lieferkette so häufig erfolgreich.
Stufe -1: Gegenmaßnahmen im Nachhinein
Große Unternehmen wie Google beschäftigen Entwickler, die Pakete systematisch prüfen und ihre Ergebnisse veröffentlichen. Das ist die Ausnahme, nicht die Regel.
Die am häufigsten diskutierte Maßnahme ist Sandboxing, etwa der Vorschlag, prozedurale Makros über WebAssembly einzusperren. Das Konzept ist sympathisch, skaliert in der Praxis aber selten: Es wird abgeschaltet, weil es bremst, oder weil die Konfiguration zu mühsam ist. Es ist, als würde man einem Esel Make-up auftragen, ein Einhorn wird trotzdem keines daraus.
Stufe 0: Sicherheit von Anfang an
Erst hier beginnt tragfähiger Boden. Sicherheit durch Design bedeutet in diesem Kontext: die Zahl externer Abhängigkeiten reduzieren und, fast noch wichtiger, die Zahl der Menschen, die über diese Abhängigkeiten Code in Ihre eigene Codebasis einbringen können.
Es soll schwer sein, ungebetenen Code in ein Projekt zu bekommen. Und es braucht wirksame Kontrollen gegen manipulierte Abhängigkeiten.
Das Vorbild: die Programmiersprache Go
Die gute Nachricht ist, dass ein Goldstandard bereits existiert.
Eine umfassende Standardbibliothek
Die wirksamste Verteidigung ist eine umfassende, von Fachleuten gepflegte Standardbibliothek. Sprachen, in denen man Hunderte Pakete von Hunderten Autoren importieren muss, nur um einen einfachen HTTP-Server zu schreiben, das neue "Hallo Welt", sind aus Angreifersicht ideal.
Gegen eine kleine Standardbibliothek wird oft ins Feld geführt, sie verhindere Stagnation. Dagegen sprechen drei Punkte. Erstens Stabilität: Viele Fremdabhängigkeiten bedeuten mehr Breaking Changes und mehr Beschäftigungstherapie. Zweitens ein zersplittertes Ökosystem: Programmiersprachen sind Plattformen, keine Produkte; sie sollen Probleme lösen helfen, statt Entwickler alle paar Wochen zwischen Framework X und Framework Y wählen zu lassen. Drittens Sicherheit: Abhängigkeiten müssen oft genug aktualisiert werden, um Schwachstellen zu schließen, aber nicht so blind, dass Hintertüren unbemerkt durchrutschen. Eine Standardbibliothek schafft eine gemeinsame Basis, geschrieben von bezahlten Fachleuten, mit mehr Augenpaaren darauf und einer deutlich höheren Hürde für Hintertüren.
Pakete direkt aus der Quelle
Ein Detail des xz-Angriffs verdient Beachtung: Die Opfer luden vorgefertigte Release-Archive statt der Quellen. So konnte der Angreifer Dateien unterbringen, die im Git-Repository schlicht nicht existierten.
Go verzichtet von Beginn an auf eine zentrale Registry. Man verweist direkt auf den Quellcode:
import (
"example.com/myorganization/mypackage"
)
Die Prüfsummen-Datenbank
Dezentral heißt allerdings nicht automatisch vertrauenswürdig. Niemand möchte, dass eine kompromittierte Plattform der kleinen Firma das saubere Paket A ausliefert und dem Großkonzern dasselbe Paket mit Hintertür.
Die Lösung des Go-Teams ist elegant: eine Prüfsummen-Datenbank. Ein unabhängiger Dienst erlaubt es, automatisch zu prüfen, ob die Prüfsumme des heruntergeladenen Pakets mit derjenigen übereinstimmt, die alle anderen erhalten. Bei Abweichung schlägt die Installation fehl.
Der Vorschlag für Rust: stdx
Rust braucht eine erweiterte Standardbibliothek, nennen wir sie stdx. Sie sollte unabhängig von der Sprache sein, damit sie sich im eigenen Takt entwickeln und bei Bedarf brechende Änderungen vornehmen kann.
Denkbar ist ein Monorepo als Cargo-Workspace unter der GitHub-Organisation von Rust, bestehend aus jenen Crates, die Entwickler täglich benötigen. Ein solches Projekt existiert bereits unter rust-stdx/stdx.
Pflaster, die heute schon helfen
Die schlechte Nachricht: Es gibt keine Anzeichen dafür, dass der Kurs sich kurzfristig ändert. Sie müssen Ihr Schicksal also selbst in die Hand nehmen und den Explosionsradius eines künftigen Angriffs begrenzen.
Dev Containers als Sandkasten
Wenn Sie nur eine einzige Sache umsetzen: Nutzen Sie Dev Containers. Ein paar Konfigurationsdateien im Repository genügen, die Einrichtung dauert unter zehn Minuten und ist wartungsarm.
Der Sicherheitsgewinn ist erheblich. Versteckt sich Schadcode in Ihren Abhängigkeiten, kommt er nicht an Ihre sensiblen Dateien. Läuft ein KI-Agent Amok, kann er kein rm -rf / auf Ihrem System ausführen. Sie schließen den Editor, löschen den Container, klonen erneut und machen weiter, als wäre nichts gewesen.
Das Ganze ist vollständig freiwillig und kennt keinen Nachteil: Kollegen, die lieber gefährlich leben, haben immerhin das Dockerfile als Referenz für die benötigte Werkzeugkette. Container-Sandboxing ist nicht kugelsicher, aber unendlich viel besser als nichts.
Passwortmanager statt Schlüssel auf der Festplatte
Es gibt im Jahr 2026 keinen Grund mehr, SSH-Schlüssel und andere Geheimnisse offen im Dateisystem liegen zu lassen. Praktisch alle Passwortmanager unterstützen SSH-Schlüssel und die Anbindung an den ssh-agent, sodass der private Schlüssel den Manager, oder besser noch das Secure Element Ihres Rechners, niemals verlässt.
Veröffentlichen nur aus der CI/CD-Pipeline
Neue Paketversionen sollten ausschließlich aus einer Pipeline wie GitHub Actions veröffentlicht werden. Damit liegen die Tokens gar nicht erst auf Ihrer Maschine, und Geheimnisse aus einer CI-Umgebung lassen sich deutlich schwerer abziehen als Dateien von einem Entwicklerrechner. Gute Entwicklungspraxis ist es ohnehin.
Abhängigkeiten direkt aus Git beziehen
Cargo erlaubt es, die Herkunft einer Abhängigkeit zu überschreiben. Üblicherweise nutzt man das für private Forks, es funktioniert aber ebenso, um statt crates.io direkt das Git-Repository anzuzapfen:
[dependencies]
serde = { version = "1", features = ["derive"] }
[patch.crates-io]
serde = { git = "https://github.com/serde-rs/serde", rev = "fa7da4a9" }
Damit der Aufwand beherrschbar bleibt, eignet sich diese Strategie vor allem für weniger populäre Pakete, also genau dort, wo eine Kompromittierung der Gemeinschaft am längsten verborgen bliebe.
Abhängigkeiten prüfen lassen
Die Rust-Gemeinschaft kennt die Schwächen des Systems und hat zwei Werkzeuge geschaffen. cargo-audit gleicht Ihre Abhängigkeiten mit einer Datenbank bekannter Schwachstellen und aufgegebener Pakete ab. cargo-vet stellt sicher, dass Fremdpakete von vertrauenswürdigen Stellen geprüft wurden. Zumindest Google und Mozilla veröffentlichen ihre Prüfergebnisse, sodass Sie diese importieren können.
Was der Bau von stdx gelehrt hat
Ein solches Projekt ist keine Kleinigkeit und wird realistisch Jahre bis zur Reife brauchen. Zwei technische Erkenntnisse sind auch für eigene Bibliotheken wertvoll.
Ein Typ für Mikrocontroller und Großserver
Rust läuft auf Controllern mit 32 KB Arbeitsspeicher ebenso wie auf Servern mit einem Terabyte. Der Schlüssel zu Bibliotheken, die beide Welten bedienen, liegt in Typen wie SmallVec<T, N>: Bis zu N Elemente liegen direkt auf dem Stack, darüber hinaus wird wie gewohnt allokiert.
struct Token {
id: SmallVec<u8, 16>,
}
Auf dem Mikrocontroller, wo eine Kennung nie länger als 16 Byte wird, allokiert diese Struktur nie, auf dem Server funktioniert sie trotzdem mit benutzerdefinierten Kennungen. Der zweite Kniff sind Schnittstellen mit aufrufergestelltem Puffer: fn encode(dst: &mut [u8], data: &[u8]) statt einer Rückgabe von Vec<u8>. Für asynchronen Code empfiehlt sich ein Sans-IO-Kern mit dünnen Hüllen für tokio und embassy; damit deckt man rund 99 Prozent der Anwendungsfälle ab.
Fazit
Die schnelle Reaktion auf den Angriff war vorbildlich, ändert aber nichts an der Statik des Gebäudes. Solange eine schmale Standardbibliothek Hunderte transitive Abhängigkeiten erzwingt und eine zentrale Registry als Schattenzone zwischen Autor und Nutzer steht, bleibt der nächste Vorfall eine Frage der Zeit, nicht der Wahrscheinlichkeit.
Die beschriebenen Maßnahmen wehren die meisten Bedrohungen ab, Dev Containers, Passwortmanager, Veröffentlichung aus der Pipeline, geprüfte Abhängigkeiten. Doch in der Sicherheit folgt immer ein "aber": Angreifer werden Dutzende weiterer Wege finden. Das ist kein Grund zur Resignation, sondern der Grund, warum sich die Diskussion über eine erweiterte Standardbibliothek lohnt.
Häufige Fragen
Sind andere Programmiersprachen sicherer als Rust?
Go gilt in dieser Hinsicht als Goldstandard, wegen seiner umfassenden Standardbibliothek, dem Verzicht auf eine zentrale Registry und der Prüfsummen-Datenbank. JavaScript teilt hingegen praktisch dieselben strukturellen Schwächen wie Rust. Die Sprache selbst ist dabei nicht das Problem, sondern die Architektur der Paketverwaltung.
Reicht es nicht, einfach Sandboxing zu aktivieren?
Sandboxing hilft, löst das Problem aber nicht. In der Praxis wird es häufig abgeschaltet, weil es Entwickler ausbremst oder die Konfiguration zu aufwendig ist. Es begrenzt den Schaden, ändert aber nichts daran, dass fremder Code überhaupt erst in Ihr Projekt gelangt.
Warum ist es problematisch, wenn Paketinhalt und Repository nicht übereinstimmen?
Weil dann niemand prüfen kann, was tatsächlich ausgeliefert wird. Bei rund 17 Prozent der beliebtesten Crates ist das der Fall. Genau diese Lücke nutzte auch der xz-Angriff, bei dem sich zusätzliche Dateien im Release-Archiv befanden, die im Git-Repository nie existierten.
Kategorien