Etwa 70 Prozent der Sicherheitslücken, die Microsoft jährlich behebt, sind Speichersicherheitsfehler – Buffer‑Overflows, Use‑after‑Free‑Fehler, Null‑Pointer‑Dereferenzierungen. Diese Zahl wird von Google für Chrome und von der NSA in ihrer Empfehlung von 2022 für speichersichere Sprachen bestätigt. Da solche Fehler aus fehlgeleiteten Speicherzugriffen resultieren, die die Hardware nicht abfangen kann, bilden sie die dominierende Kategorie ausnutzbarer Schwachstellen in Systemsoftware.
C und C++ geben Programmierern direkte Kontrolle über den Speicher, was Performance ermöglicht, jedoch auch zu Fehlern führt, die Compilern entgehen: Schreiben über das Ende eines Puffers hinaus, Zugriff auf Speicher nach seiner Freigabe, Lesen von nicht initialisiertem Speicher. Da diese Fehler erst zur Laufzeit auftreten und von der statischen Analyse häufig nicht erfasst werden, klassifizieren sie das Ergebnis als undefiniertes Verhalten, das zu Abstürzen, falschen Ergebnissen oder Ausnutzung durch Angreifer führen kann.
Rust und Go eliminieren diese Fehler durch ihr Design. Rust tut dies, indem es Besitz‑ und Lebenszeitregeln bereits beim Kompilieren durchsetzt; Go verwendet einen Garbage Collector und verbietet gefährliche Pointer‑Arithmetik, wodurch die häufigsten Quellen undefinierten Verhaltens ausgeschlossen werden. Daten von Google, Microsoft und der NSA belegen, dass speichersichere Sprachen dort besonders sinnvoll sind, wo die genannten Risiken auftreten. Die praktische Frage für Ingenieurteams lautet: Wann rechtfertigen die Adoptionskosten den Aufwand?
Wie Rust und Go Speichersicherheit erreichen
Sie verfolgen unterschiedliche Ansätze mit jeweils eigenen Trade‑offs.
Rust: Besitz und der Borrow‑Checker
Rusts Speichersicherheit stammt von seinem Besitzsystem, das zur Compile‑Zeit durchgesetzt wird. Jeder Wert hat genau einen Besitzer. Wenn der Besitzer den Gültigkeitsbereich verlässt, wird der Wert freigegeben. Dadurch wird verhindert, dass ein Zeiger nach dem Freigabezeitpunkt noch verwendet wird, was sonst zu Use‑After‑Free führen könnte. Referenzen (Borrows) werden zur Compile‑Zeit überprüft, um sicherzustellen, dass sie den Wert, auf den sie verweisen, nicht überleben und dass mutable und immutable Referenzen nicht gleichzeitig existieren.
fn process(data: Vec<u8>) -> usize {
// data wird hier besessen
let length = data.len();
// data wird am Ende des Gültigkeitsbereichs freigegeben – kein manuelles freigeben nötig
length
}
fn main() {
let buffer = vec![1, 2, 3, 4, 5];
let len = process(buffer);
// buffer wurde in process verschoben – hier zu verwenden ist ein Compile‑Fehler
// println!("{:?}", buffer); // ❌ Fehler: Wert nach Move verwendet
println!("Länge: {}", len);
}
Der Borrow‑Checker verhindert Use‑After‑Free und Datenrassen zur Compile‑Zeit, so dass diese Probleme nicht auftreten können. Es gibt keinen Garbage Collector – Speicher wird deterministisch verwaltet. Da die Freigabe exakt zum Ende des jeweiligen Gültigkeitsbereichs erfolgt, lassen sich Latenzspitzen exakt vorhersagen. Das macht Rust geeignet für Systemprogrammierung, eingebettete Systeme und latenzempfindliche Anwendungen, bei denen Garbage‑Collector‑Pausen inakzeptabel sind.
Die Kosten sind die Lernkurve. Der Borrow‑Checker lehnt Code ab, der in C++ gültig, aber unsicher wäre. Das Erlernen von Code, der den Borrow‑Checker befriedigt, dauert Wochen bis Monate für erfahrene Ingenieure. Der Compiler gibt detaillierte und hilfreiche Fehlermeldungen, aber das mentale Modell ist tatsächlich anders als bei den meisten anderen Sprachen.
Go: Garbage Collection mit sicheren Standardwerten
Go erreicht Speichersicherheit durch einen Garbage Collector und sichere Standardwerte: keine Pointer‑Arithmetik, bounds‑geprüfter Array‑Zugriff, Nil‑Pointer‑Erkennung zur Laufzeit statt undefiniertem Verhalten. Der Garbage Collector übernimmt die gesamte Speicherfreigabe, sodass Entwickler nicht mehr versehentlich einen bereits freigegebenen Block adressieren können. Go verhindert nicht alle Speicherfehler – Nil‑Pointer‑Dereferenzierungen verursachen immer noch Panics – aber es eliminiert die Klasse von undefiniertem Verhalten, die C/C++‑Schwachstellen ausnutzbar macht.
func processItems(items []string) int {
total := 0
for _, item := range items {
total += len(item)
}
return total
// Speicher wird vom Garbage Collector verwaltet – kein manuelles freigeben notwendig
}
Gos Lernkurve ist deutlich niedriger als Rusts. Ingenieure, die mit Python, Java oder TypeScript vertraut sind, können innerhalb von Tagen in Go produktiv sein. Der Garbage Collector führt gelegentliche Pausen ein, aber Gos Garbage‑Collector hat sich erheblich verbessert – Sub‑Millisekunden‑Pausen sind für die meisten Workloads typisch. Für die meisten Service‑Anwendungen sind diese Pausen vernachlässigbar.
Wo jede Sprache gewinnt
| Kriterium | Rust | Go |
|---|---|---|
| Speichersicherheit | Compile‑Zeit, kein Garbage Collector | Laufzeit, Garbage Collector verwaltet |
| Performance | C/C++ vergleichbar | ~2–5× langsamer als Rust für CPU‑gebundene Arbeit |
| Latenz‑Vorhersagbarkeit | Deterministisch (keine Garbage‑Collector‑Pausen) | Sub‑ms Garbage‑Collector‑Pausen typisch |
| Lernkurve | Hoch (Wochen bis Monate) | Niedrig (Tage bis Wochen) |
| Nebenläufigkeitsmodell | Besitz verhindert Datenrassen zur Compile‑Zeit | Goroutines + Channels; Datenrassen zur Laufzeit erkannt |
| Ökosystemreife | Wächst schnell; stark für Systeme/Embedded/Wasm | Reif; stark für Services, CLIs, Infrastruktur‑Tools |
| Bestes Anwendungsfeld | Systemsoftware, Embedded, Wasm, Krypto, OS‑Komponenten | Web‑Services, CLIs, DevOps‑Tooling, Netzwerkdienste |
Reale Adoption
Das Adoptionssignal ist am stärksten in Infrastruktur‑ und sicherheitskritischer Software:
Rust in der Produktion:
- Der Linux‑Kernel akzeptierte Rust 2022 als zweite Implementierungssprache. Damit lässt sich Rust nahtlos in den Kernel integrieren. Rust‑Treiber sind jetzt im Mainline‑Kernel.
- Android: Google berichtet, dass der Prozentsatz neuer Speichersicherheitsfehler in Android von 76 % im Jahr 2019 auf 24 % im Jahr 2024 gesunken ist, korrelierend mit erhöhter Rust‑Adoption in neuem Code.
- AWS hat Rust für Firecracker (die MicroVM‑Laufzeit hinter Lambda und Fargate), Bottlerocket (ein container‑optimiertes OS) und Teile des S3‑Storage‑Stacks verwendet.
Go in der Produktion:
- Docker, Kubernetes, Terraform, Prometheus und Grafana sind alle in Go geschrieben.
- Go ist die dominante Sprache für Cloud‑native Infrastruktur‑Tooling.
- Cloudflare, Dropbox und Uber haben Fallstudien zur Go‑Adoption im großen Maßstab veröffentlicht.
Wann sich die Adoptionskosten lohnen
Die Adoptionskosten sind real: Einstellung ist schwieriger (weniger Ingenieure kennen Rust als Python oder Java), Onboarding dauert länger, und das Ökosystem ist für einige Bereiche kleiner. Der Markt für erfahrene Rust‑Entwickler ist noch im Aufbau, sodass Unternehmen länger suchen müssen. Die Kosten lohnen sich, wenn:
Sicherheit ein primäres Anliegen ist. Wenn Ihre Software unvertrauenswürdige Eingaben verarbeitet, kryptografisches Material verarbeitet oder in einem sicherheitskritischen Kontext läuft, ist die Eliminierung von Speichersicherheitsfehlern eine erhebliche Investition wert. Die NSA‑Leitlinien von 2022 empfehlen ausdrücklich Rust, Go, Java, C# und Swift gegenüber C und C++ für neue Entwicklung. Da Angreifer häufig genau diese Schwachstellen ausnutzen, reduziert die Vermeidung von Speicherfehlern das Risiko von Remote‑Code‑Execution signifikant.
Performance‑Anforderungen streng sind. Rusts Zero‑Cost‑Abstraktionen und deterministische Speicherverwaltung machen es mit C++ für CPU‑gebundene und latenzempfindliche Arbeit konkurrenzfähig. Insbesondere bei Parsern, Codecs oder Netzwerk‑Protokoll‑Stacks zeigen Benchmarks, dass Rust kaum Overhead gegenüber reinem C erzeugt.
Sie Infrastruktur‑Tooling schreiben. Gos Nebenläufigkeitsmodell (Goroutines sind günstig; Channels sind idiomatisch), schnelle Kompilierung und Single‑Binary‑Deployment machen es gut geeignet für CLIs, Daemons und Services. Die Möglichkeit, ein komplettes Tool als einzige ausführbare Datei zu verteilen, verkürzt den Deploy‑ und Wartungsaufwand erheblich.
Die Adoptionskosten lohnen sich nicht, wenn:
- das Team klein und die Domain nicht sicherheitskritisch ist; in einem kleinen Team fehlt oft die Zeit, neue Paradigmen zu vermitteln.
- der Engpass Geschäftslogik ist, nicht Systemperformance; wenn die Engpässe im Geschäftslogik‑Layer liegen, profitieren Sie kaum von den systemnahen Optimierungen.
- die bestehende Sprache (TypeScript, Python, Java) ausreichend ist und das Team darin produktiv ist; ein etablierter Stack, in dem das Team bereits hohe Produktivität erreicht, macht den Umstieg unnötig.
- der Einstellungspool für die neue Sprache zu dünn ist, um das Team zu erhalten; ein dünner Talentpool bedeutet, dass die Fluktuation das Projekt gefährden kann.
Warum der Borrow‑Checker anfänglich schwer erscheint
Ingenieure, die von C++, Java oder Python kommen, empfinden den Rust‑Borrow‑Checker in den ersten Wochen oft als frustrierend. Die Frustration hat eine spezifische Ursache: Der Borrow‑Checker lehnt Muster ab, die in anderen Sprachen gültig sind, in Rusts Besitzmodell aber unsicher wären. Der mentale Modellwechsel betrifft nicht das Erlernen neuer Syntax – sondern das Erlernen, Programme so auszudrücken, dass Besitz explizit gemacht wird. So muss man beispielsweise klar angeben, welche Funktion den Besitz übernimmt und welche nur temporär lesen darf; das verhindert stillschweigend entstehende Datenrassen.
Der Borrow-Checker erfordert eine vorzeitige Kompilationszeit, um die Gesamtleistung und die Vertrauensstufe im nebenläufigen Code zu gewährleisten. Ingenieure, die Rust in der Produktion eingesetzt haben, bestätigen, dass die reduzierte Debugging-Zeit und das erhöhte Vertrauen in nebenläufige Speicheroperationen einen ausgeglichenen Wertbeitrag liefern.
Gos Lernkurve ist niedriger, weil dieser mentale Modellwechsel nicht erforderlich ist. Der Garbage Collector verwaltet den Speicher; das Typsystem ist vertraut. Der Trade‑off: Gos Sicherheitsgarantien sind schwächer – Nil‑Pointer‑Dereferenzierungen verursachen immer noch Laufzeit‑Panics, und Datenrassen werden zur Laufzeit statt zur Compile‑Zeit erkannt. Für die meisten Service‑Workloads ist dieser Trade‑off akzeptabel. Für Systemsoftware, bei der Korrektheitsgarantien wichtiger sind als Entwicklungsgeschwindigkeit, sind Rusts stärkere Garantien die Investition wert.
Der schrittweise Adoptionspfad
Weder Rust noch Go erfordern eine vollständige Neuentwicklung. Beide haben gute FFI‑Unterstützung (Foreign Function Interface) für den Aufruf von C‑Bibliotheken, und beide können von anderen Sprachen aufgerufen werden. Ein gängiges Muster ist, zuerst die sicherheitskritischen oder performance‑kritischen Komponenten neu zu schreiben:
- Eine C‑Erweiterung in einem Python‑Dienst durch eine Rust‑Erweiterung ersetzen (via PyO3) – so kann ein Python‑Service weiterhin laufen, profitiert aber von Rust‑sicherer Verarbeitung.
- Eine performance‑kritische Parsing‑ oder Encoding‑Bibliothek in Rust neu schreiben, aufgerufen von der bestehenden Anwendung – die Rust‑Bibliothek liefert deterministische Performance, während die übrige Anwendung unverändert bleibt.
- Neue CLIs und Infrastruktur‑Tools in Go schreiben, während bestehende Dienste in ihrer aktuellen Sprache bleiben – neue Tools lassen sich sofort als einzelne Binärdatei ausliefern, ohne dass ein komplexes Laufzeit‑Environment nötig ist.
NSA-Leitlinien fordern nicht, dass alles in Rust umgeschrieben wird. Sie empfehlen vielmehr, speichersichere Sprachen für neue Entwicklung anzuwenden, wo dies sinnvoll ist, und die Migration der sicherheitskritischsten Komponenten zu priorisieren.

Comments (0)
Please sign in to leave a comment