Dos and Don'ts beim Programmieren Safety und Security in Rust

Von Ingo Budde* 9 min Lesedauer

Anbieter zum Thema

Rust verfügt über zahlreiche Mechanismen, die Entwickler dabei unterstützen, Fehler sowie Sicherheitsschwachstellen zu vermeiden. Dennoch ist auch hiermit eine robuste Safety und Security nicht garantiert. Wer sichere Software in Rust entwickeln möchte, sollte die folgenden Dos & Don'ts beachten.

Die Programmiersprache Rust garantiert Speichersicherheit zur Compile-Zeit und wird zunehmend für die Entwicklung von eingebetteten Systemen genutzt. Dennoch zeigt die Praxis, dass auch unter der Anwendung von Rust vielfältige Sicherheitsprobleme auftreten können. Wir geben einen Überblick über die Dos and Don'ts bei der Umsetzung von sicherheitsrelevanten Funktionen mit Rust. (Bild:  KI-generiert / DALL-E)
Die Programmiersprache Rust garantiert Speichersicherheit zur Compile-Zeit und wird zunehmend für die Entwicklung von eingebetteten Systemen genutzt. Dennoch zeigt die Praxis, dass auch unter der Anwendung von Rust vielfältige Sicherheitsprobleme auftreten können. Wir geben einen Überblick über die Dos and Don'ts bei der Umsetzung von sicherheitsrelevanten Funktionen mit Rust.
(Bild: KI-generiert / DALL-E)

Angesichts der weiter zunehmenden Vernetzung von Systemen und der kommenden Regularien der Europäischen Union (EU), allen voran der Cyber Resilience Act und die EU-Maschinenverordnung, werden die Erhöhung der Angriffssicherheit (Security) und die Vermeidung von negativen Auswirkungen von Security-Vorfällen auf die funktionale Sicherheit (Safety) von eingebetteten Systemen essenziell. Der Cyber Resilience Act verlangt von Softwareherstellern ein effektives Security Testing, um Sicherheitslücken zu vermeiden.

Die derzeit am häufigsten offengelegten Sicherheitslücken sind sogenannte Speicher-Sicherheitslücken. [1, 2, 3, 4, 5]. Sie treten auf, wenn eine Software den Arbeitsspeicher manuell verwaltet und sich dabei ein Softwarefehler einschleicht. Diese Sicherheitslücken führen oft zu Programmabstürzen, wie beispielsweise im Falle des kürzlich aufgetretenen Fehlers in der Software Crowdstrike Falcon [6] und werden von böswilligen Akteuren routinemäßig für kriminelle Handlungen ausgenutzt (Stichwort Cybercrime). Softwarehersteller sind dadurch gezwungen, ständig Sicherheitsupdates zu veröffentlichen und Kunden müssen kontinuierlich Patches einspielen. Historische Bemühungen der Softwarehersteller dieses Problem mithilfe von Security Testing, Security Patches oder Schulungen zu lösen, konnten das Problem bisher nicht gänzlich ausmerzen. Die Softwareindustrie setzt deshalb vermehrt auf Sprachen, die von Haus aus speichersicher sind, wie beispielsweise die Sprache Rust.

Sicherheit in Rust

Die Sprache Rust ist bekannt für ihr starkes Typsystem, welches Speichersicherheit und Thread-Sicherheit zur Compile Zeit sicherstellt. Rust verwendet keinen Garbage Collector und gewinnt deshalb auch für die Entwicklung eingebetteter Systeme an Bedeutung.

Die Sicherheit in Rust beruht darauf, dass der Compiler eine Reihe von Einschränkungen erzwingt: Er verbietet beispielsweise unsichere Pointer-Operationen sowie mehrere schreibbare Referenzen auf dieselbe Speicherstelle. Außerdem sind veränderliche globale Variablen standardmäßig verboten.

Die Praxis hat gezeigt, dass Entwickler trotz dieser drastischen Einschränkungen viele Probleme in Rust ausdrücken können. Es lässt sich jedoch nicht jedes Problem im Rahmen dieses sicheren Typsystems ausdrücken und für spezielle Fälle bietet Rust eine Unsafe-Sprache, welche diese Restriktionen aufhebt und potenziell gefährliche Operationen erlaubt. Der Programmierer ist nun wieder selbst in der Verantwortung, dass die Regeln der Sprache eingehalten werden und undefiniertes Verhalten verhindert wird. Schlimmer noch: Das starke Typsystem von Rust beruht auf weitaus mehr Regeln als beispielsweise das von C++. Daher muss man beim Einsatz von Unsafe Rust umso mehr aufpassen, dass keine dieser Rust-Regeln verletzt wird, denn der restliche Safe Rust Code verlässt sich auf diese Regeln. Nachfolgend beschreiben wir, mit welchen Werkzeugen es dennoch möglich ist, sicher mit Rust zu entwickeln.

Dos and Don'ts für die Entwicklung mit Unsafe Rust

Unsafe Rust sollte nicht eingesetzt werden, wenn es eine sichere API für die gesuchte Funktionalität gibt. Die Rust Standard-Bibliothek bietet bereits viele sichere APIs an, welche intern zwar teils Unsafe Rust nutzen, für die die Korrektheit aber sichergestellt wurde. Zusätzlich gibt es viele Rust-Bibliotheken, die ebenfalls Unsafe Rust kapseln und über eine sichere API anbieten. Es lohnt sich daher ein Blick in die Rust Package Registry [7], welche derzeit ca. 160.000 Open-Source Softwarepakete bereitstellt.

Schreibt man Unsafe Rust selbst, so empfiehlt es sich, den Code mit entsprechender Sorgfalt zu pflegen. Dabei hat sich im Rust Ecosystem der Ansatz des „Interior Unsafe“ [8] also gekapseltem Unsafe Code durchgesetzt. Demnach sollte man den Unsafe Rust Code möglichst in eine eigene Funktion oder ein eigenes Projekt auslagern, um ihn besser separat warten zu können. Außerdem können Werkzeuge genutzt werden, die bei der sicheren Entwicklung unterstützen.

Werkzeuge zum Erkennen von Schwachstellen im eigenen Code

Das Werkzeug Miri [9] führt eine dynamische Analyse des Programms durch, die den Quellcode interpretiert und so zur Laufzeit ungültige Speicherzugriffe, Speicherleaks sowie undefiniertes Verhalten identifiziert. Treten Fehler auf, so kann Miri auf das Problem hinweisen. Miri wurde bereits erfolgreich eingesetzt, um Fehler in einer Reihe von bestehenden Rust Projekten aufzudecken.

Das Werkzeug Cargo Fuzz [10] kann verwendet werden, um Rust Programme mithilfe eines Fuzzers zur Laufzeit zu analysieren. Der Fuzzer führt das Programm mit exotischen Parametern aus mit dem Ziel, ungetestete Pfade auszuführen, die das Programm zum Absturz bringen. Gelingt dies, so wird der Fehler dem Nutzer berichtet.

Das Werkzeug MirChecker [11] durchsucht den Rust Quellcode nach Fehlern mithilfe einer statischen Analyse. Anders als der Rust Compiler bezieht es dabei aber explizit auch Speicherfehler im Unsafe Rust mit ein.

Das Werkzeug MIRAI [12] findet vergleichbare Fehler im Rust Quellcode, unterstützt aber auch eine Timing-Analyse, die genutzt werden kann, um Seitenkanal-Schwachstellen zu identifizieren. Diese Schwachstellen ermöglichen es, Rückschlüsse auf interne Programmabläufe zu ziehen und wurden bereits eingesetzt [13], um einen geheimen Schlüssel aus einem Security Token zu extrahieren.

Jetzt Newsletter abonnieren

Verpassen Sie nicht unsere besten Inhalte

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung. Die Einwilligungserklärung bezieht sich u. a. auf die Zusendung von redaktionellen Newslettern per E-Mail und auf den Datenabgleich zu Marketingzwecken mit ausgewählten Werbepartnern (z. B. LinkedIn, Google, Meta).

Aufklappen für Details zu Ihrer Einwilligung

Das Werkzeug Rudra [14] führt ebenfalls eine statische Analyse durch, fokussiert aber auf spezielleren Klassen von Problemen im Kontext von Unsafe Rust: Es erkennt beispielsweise fehlerhafte Programmzustände, wenn ein Programmabbruch (Panic) innerhalb von Unsafe Code auftritt.

Die genannten Werkzeuge zur Erkennung von Schwachstellen im eigenen Code sind Open Source und frei verwendbar. Miri und Cargo Fuzz führen das Programm aus und können deshalb nur Fehler identifizieren, in die das Programm zur Laufzeit hineinläuft. Die anderen Werkzeuge erkennen Fehler im Quellcode und ermöglichen daher eine bessere Abdeckung, können aber potenziell Fehlmeldungen liefern. Wir empfehlen für die Entwicklung von sicherheitskritischer Software die Verwendung aller genannten Werkzeuge.

Werkzeuge zum Erkennen von Schwachstellen in Softwareabhängigkeiten

Fehler in Softwareabhängigkeiten können sich auf alle Programme auswirken, die sie verwenden. Es gibt Werkzeuge, die Fehler in Abhängigkeiten erkennen können.

Das Werkzeug Cargo Geiger [15] identifiziert Unsafe Code in Softwareabhängigkeiten. Dadurch wird es möglich, Abhängigkeiten danach auszuwählen, ob und wieviel Unsafe Code sie enthalten.

Die RustSec Datenbank [16] dokumentiert bekannte Fehler in veröffentlichten Rust Bibliotheken und wird von Security Testing Werkzeugen teils automatisiert genutzt, um auf die problematische Nutzung fehlerhafter Bibliotheken hinzuweisen. Beispielsweise führte ein Speicherfehler in der viel-genutzten Rust-Bibliothek bumpalo zu einer Use-After-Free Vulnerability [17]. Die RustSec Datenbank dokumentiert sowohl die betroffenen Versionen der Bibliotheken, sowie die verwundbaren Funktionen.

Das Werkzeug Cargo Audit [18] kann genutzt werden, um die genutzten Abhängigkeiten eines Rust Projektes in der RustSec Datenbank zu suchen. Es meldet, wenn genutzte Bibliotheken von Schwachstellen betroffen sind. Falls eine verwendete Bibliothek eine bekannte Sicherheitsschwachstelle hat, so wird dem Nutzer empfohlen auf eine Version zu updaten, die den Fehler nicht mehr enthält – falls eine solche Version existiert. Cargo Audit überprüft allerdings nicht, ob der Rust Code die fehlerhafte Funktion überhaupt aufruft. Dennoch ist das Werkzeug hilfreich, um auszuschließen, dass Abhängigkeiten genutzt werden, für die Schwachstellen bekannt sind.

Das Werkzeug OSV-Scanner [19] von Google findet ebenfalls Softwareabhängigkeiten mit bekannten Schwachstellen. Es unterstützt aber auch eine experimentelle Funktionalität, die den Quellcode analysiert, um herauszufinden, ob die verwundbare Funktion überhaupt aufgerufen wird. Dies kann bei der Entscheidung unterstützen, ob der Quellcode tatsächlich verwundbar ist oder nicht.

Das Fraunhofer IEM entwickelt im Rahmen von Forschungsprojekten das Werkzeug Cargo Flowcheck. Ziel ist es, eindeutig zu erkennen, ob ein Rust Programm eine von Angreifern ausnutzbare Schwachstelle hat oder nicht. Cargo Flowcheck prüft dafür die Softwareabhängigkeiten und durchleuchtet den Quellcode intensiv, indem es dabei auch mögliche Belegungen von Variablenwerten einbezieht. So lässt sich präziser entscheiden, ob das Programm verwundbar ist oder nicht. Dies kann einem Softwareentwickler helfen, die Kritikalität einer Verwundbarkeit einzustufen und beispielsweise zu entscheiden, ob ein Security Patch dringend notwendig ist oder ob ein Fix im Zuge der nächsten stabilen Version ausreicht.

Die genannten Werkzeuge zur Erkennung von Schwachstellen in Softwareabhängigkeiten sind Open Source und frei verwendbar und wir empfehlen deren Nutzung. Die Wahrscheinlichkeit für Fehlmeldungen ist hier gering.

Ausblick und Zusammenfassung

Der kürzlich verabschiedete Cyber Resilience Act und die neue EU-Maschinenverordnung fordern, dass die Sicherheit schon bei der Softwareentwicklung berücksichtigt wird (Security by Design). Die Softwareindustrie setzt zunehmend auf speichersichere Sprachen. Die Sprache Rust verhindert Speicherfehler beim Kompilieren durch strenge Sprachregeln. Der Compiler wird ständig weiterentwickelt und seine Analysen werden präziser [20], sodass es komfortabler wird mit Rust zu entwickeln. Wir empfehlen den Einsatz von zusätzlichen Werkzeugen [9-12,14] um auch den Unsafe Code zu prüfen und weitere Werkzeuge [15,18,19] um bekannte Schwachstellen in verwendeten Softwareabhängigkeiten zu erkennen. Das Fraunhofer IEM entwickelt in seiner Forschung das Tool Cargo Flowcheck, mit dem eine noch viel präzisere Erkennung von Schwachstellen in Softwareabhängigkeiten als bisher möglich werden soll. (sg)

Literaturverzeichnis

[1] MITRE CORPORATION: CWE Top 25 Most Dangerous Software Weaknesses. Unter: https://cwe.mitre.org/top25/archive/2023/2023_top25_list.html, 11. Oktober 2024

[2] MICROSOFT: A proactive approach to more secure code, 2019

[3] GOOGLE: Chromium Security – Memory safety. Unter: https://www.chromium.org/Home/chromium-security/memory-safety/, 11. Oktober 2024

[4] MOZILLA: Implications of Rewriting a Browser Component in Rust. Unter: https://hacks.mozilla.org/2019/02/rewriting-a-browser-component-in-rust/, 11. Oktober 2024

[5] U. S. CYBERSECURITY AND INFRASTRUCTURE SECURITY AGENCY; U. S. NATIONAL SECURITY AGENCY; AUSTRALIAN CYBER SECURITY CENTRE; CANADIAN CENTRE FOR CYBER SECURITY; UNITED KINGDOM NATIONAL CYBER SECURITY CENTRE; NEW ZEALAND NATIONAL CYBER SECURITY CENTRE; COMPUTER EMERGENCY RESPONSE TEAM NEW ZEALAND: The Case for Memory Safe Roadmaps: Why Both C-Suite Executives and Technical Experts Need to Take Memory Safe Coding Seriously

[6] BUNDESAMT FÜR SICHERHEIT IN DER INFORMATIONSTECHNIK: Fehlerhaftes Update von Crowdstrike Falcon. Unter: https://www.bsi.bund.de/SharedDocs/Cybersicherheitswarnungen/DE/2024/2024-257485-10F1_csw.html, 11. Oktober 2024

[7] RUST FOUNDATION: crates.io: Rust Package Registry. Unter: https://crates.io, 11. Oktober 2024

[8] QIN, B.; CHEN, Y.; YU, Z.; SONG, L.; ZHANG, Y.: Understanding memory and thread safety practices and issues in real-world Rust programs. In: Donaldson, A. F. (Ed.): Proceedings of the 41st ACM SIGPLAN Conference on Programming Language Design and Implementation. PLDI '20: 41st ACM SIGPLAN International Conference on Programming Language Design and Implementation, 15 06 2020 20 06 2020, London UK, ACM Digital Library, Association for Computing Machinery, New York,NY,United States, 2020, pp. 763–779

[9] Miri: An interpreter for Rust's mid-level intermediate representation. Unter: https://github.com/rust-lang/miri, 11. Oktober 2024

[10] cargo-fuzz: Command line helpers for fuzzing. Unter: https://github.com/rust-fuzz/cargo-fuzz, 11. Oktober 2024

[11] LI, Z.: MirChecker: A Simple Static Analysis Tool for Rust. Unter: https://github.com/lizhuohua/rust-mir-checker, 11. Oktober 2024

[12] FACEBOOK: MIRAI, 11. Oktober 2024

[13] KNOP, D.: Yubikey: Cloning-Angriff über Seitenkanal. heise online, 2024, 04.09.2024

[14] BAE, Y.: Rudra: Rust Memory Safety & Undefined Behavior Detection. Unter: https://github.com/sslab-gatech/Rudra, 11. Oktober 2024

[15] cargo-geiger: Detects usage of unsafe Rust in a Rust crate and its dependencies. Unter: https://crates.io/crates/cargo-geiger, 11. Oktober 2024

[16] RUST SECURE CODE WORKING GROUP: RustSec Advisory Database. Unter: https://rustsec.org/, 11. Oktober 2024

[17] bumpalo: Use-after-free due to a lifetime error in Vec::into_iter. Unter: https://rustsec.org/advisories/RUSTSEC-2022-0078.html, 11. Oktober 2024

[18] RUST COMMUNITY: cargo audit: Audit Cargo.lock for crates with security vulnerabilities. Unter: https://crates.io/crates/cargo-audit, 11. Oktober 2024

[19] GOOGLE: OSV - Open Source Vulnerabilities. Unter: https://osv.dev, 11. Oktober 2024

[20] RAKIC, R.; MATSAKIS, N.: Polonius update. Unter: https://blog.rust-lang.org/inside-rust/2023/10/06/polonius-update.html, 11. Oktober 2024

Dieser Beitrag wurde mit freundlicher Genehmigung des Autors aus dem Tagungsband des ESE Kongress 2024 übernommen.

* Ingo Budde ist seit 2018 Softwareentwickler und wissenschaftlicher Mitarbeiter in der Abteilung Sichere IoT-Systeme im Forschungsbereich Softwaretechnik & IT-Sicherheit des Fraunhofer IEM. In dieser Zeit bearbeitete er verschiedene Projekte mit Industrie- und Forschungspartnern u.a. aus dem Maschinen- und Anlagenbau zu den Themenfeldern Security by Design, Software Engineering und statischer Programmanalyse. Die Programmiersprache Rust hat er mehrfach eingesetzt, um die Sicherheit und Performanz der Systeme zu erhöhen. In einer wissenschaftlichen Arbeit hat er sich mit dem Thema Sicherheit im Rust-Ökosystem auseinandergesetzt und verschiedene Werkzeuge zur Erhöhung der Sicherheit untersucht sowie eine Erweiterung für eines dieser Werkzeuge entwickelt.

(ID:50505288)