BLOG | BÜRO DES CTO

Jenseits des C

Veröffentlicht am 07. Februar 2023

Seit Jahrzehnten sind C und seine unmittelbaren Nachfolger die Programmiersprachen der Wahl für die Systemprogrammierung. Als C Anfang der 1970er Jahre entwickelt wurde, stellte es einen wichtigen Fortschritt gegenüber der Assemblersprache dar. Fünfzig Jahre später können wir es besser machen.

Computersicherheit war im Jahr 1970 und auch viele Jahre danach für die meisten Menschen kein Thema. Die erste größere Sicherheitslücke im Internet trat viele Jahre später im Jahr 1988 auf. Er war als Internet-Wurm bekannt und machte sich einen Pufferüberlauf zunutze, bei dem ein Array im Speicher außerhalb seiner Grenzen indiziert wurde.  

In der C-Sprachfamilie, zu der auch C++ gehört, werden die Grenzen von Arrays nicht überprüft. Es liegt in der Verantwortung des Programmierers, sicherzustellen, dass auf das Array korrekt zugegriffen wird. Pufferüberlauffehler kommen daher häufig vor. Schlimmer noch: Es ist leicht, absichtlich einen Pufferüberlauf zu verursachen und dadurch auf Speicher zuzugreifen, auf den man nicht zugreifen sollte.

Ein Pufferüberlauf ist nur ein Beispiel für Speicherunsicherheit . Andere damit zusammenhängende Beispiele sind Zeigerarithmetik und hängende Zeiger (auch bekannt als „Use-after-free“-Bugs). Heutzutage gibt es viele Programmiersprachen, die eine Vielzahl von Techniken einsetzen, um zu gewährleisten, dass alle in diesen Sprachen geschriebenen Programme keine Probleme mit der Speichersicherheit aufweisen. Die C-Sprachfamilie bietet keine solche Garantie; Speichersicherheit war nie ein Designziel dieser Sprachen.

Etwa 70 % der Sicherheitslücken sind auf Verletzungen der Speichersicherheit zurückzuführen. Diese Behauptung wird durch überwältigende Daten gestützt. Die Speichersicherheit berücksichtigt:

  • 67 % der ausgenutzten Schwachstellen wurden offengelegt und in freier Wildbahn entdeckt (laut Schätzung von Google Project Zero 2021 ).
  • 90 % der Android-Schwachstellen ( laut Google ). Obwohl diese Zahl in letzter Zeit aufgrund der Verwendung von Rust zurückgegangen ist, hängen 89 % der über die Ferne ausnutzbaren Schwachstellen weiterhin mit der Speichersicherheit zusammen.
  • 70 % der Microsoft-Sicherheitslücken (laut Microsoft).
  • Die meisten aktuellen Fehlerbehebungen für Apple-Sicherheitslücken (für Catalina , Big Sur , Monterey , Safari , iOS , tvOS und watchOS ).

Im Juli 2022 waren 5/6 der in Chrome 103.0.5060.134 behobenen Schwachstellen Probleme mit der Speichersicherheit. Natürlich wäre es enorm hilfreich, Fehler im Zusammenhang mit der Speichersicherheit zu beseitigen. Die Branche weiß seit vielen Jahren, wie das geht: durch die Verwendung speichersicherer Programmiersprachen. Das Problem bestand historisch immer in den damit verbundenen Leistungseinbußen. Aufgrund der Bedeutung der Speichersicherheit wird an neuen Strategien gearbeitet, um diese ohne den herkömmlichen Mehraufwand zu erreichen. Heute wissen wir, wie sich Speichersicherheitsfehler mit geringen oder keinen Laufzeitkosten beseitigen lassen. Sprachen wie Rust nutzen Innovationen im Typsystemdesign, um Speichersicherheit ohne kostspielige Laufzeitunterstützung zu gewährleisten.

Dies führt zu einer unausweichlichen Schlussfolgerung: Hören Sie auf, neuen Systemcode in C/C++ zu schreiben (oder allgemeiner, in speicherunsicheren Sprachen).

Dies ist kein Aufruf zum massiven, wahllosen Umschreiben des vorhandenen Codes. Das Ersetzen vorhandener Software ist kostspielig und nicht ohne Risiken. Die Branche muss jedoch aufhören, das Problem dadurch zu verschärfen, dass sie den bestehenden Codebasen weiteren speicherunsicheren Code hinzufügt. Priorisieren Sie bei vorhandenem Code das Neuschreiben der sensibelsten Komponenten: der Komponenten, die für die Validierung oder Verarbeitung nicht vertrauenswürdiger Benutzereingaben verantwortlich sind, der Komponenten, die in einem privilegierten Kontext ausgeführt werden, der Komponenten, die außerhalb einer Sandbox ausgeführt werden usw.

Diese Position ist zwar weit verbreitet, für manche aber immer noch umstritten. Hier sind einige der häufigsten Argumente für den Status Quo und unsere Antworten darauf.

  • Fehler sind nicht spezifisch für Programmiersprachen.  
    • Die meisten Schwachstellen sind auf Fehler in der Speichersicherheit zurückzuführen. In speichersicheren Sprachen wie Rust können keine Speichersicherheitsfehler auftreten. Daher verhindert die Verwendung einer speichersicheren Sprache, dass die meisten Schwachstellen überhaupt erst entstehen.
  • Durch Codeüberprüfungen werden Fehler aufgedeckt.
    • Studien zeigen, dass Codeüberprüfungen zwar zu Verbesserungen führen können, jedoch häufig viele Probleme übersehen; jedenfalls werden dabei nicht alle Probleme gefunden.
  • Unsichere Module machen den ganzen Punkt hinfällig.
    • Unsicherer Code wird explizit in sehr kleine Codeabschnitte unterteilt, sodass die Ressourcen auf die Validierung konzentriert werden können.
  • Eine speichersichere Sprachimplementierung könnte fehlerhaft sein.
    • Das stimmt zwar, aber die Wahrscheinlichkeit ist viel geringer. Compiler sind relativ klein und werden streng getestet. Und wenn Sie einen Compiler reparieren, werden automatisch auch alle mit ihm kompilierten Programme repariert, im Gegensatz zur Behebung eines Fehlers in einem einzelnen Programm.
  • Nur unerfahrene oder unfähige Programmierer machen diese Fehler.
    • Im Gegenteil, die obigen Daten stammen aus großen Systemprojekten wie dem Chrome-Browser und den Betriebssystemen Windows und MacOS. An diesen Projekten arbeiten einige der kompetentesten und erfahrensten Entwickler der Branche, und trotzdem weisen sie weiterhin Probleme mit der Speichersicherheit auf.
  • Diese Probleme können durch Befolgen bewährter Methoden vermieden werden.
    • Siehe oben. Die genannten Teams befolgen sehr strenge Praktiken und verwenden die besten verfügbaren Werkzeuge.
  • Es ist unpraktisch, den gesamten Code neu zu schreiben.
    • Niemand plädiert für eine vollständige Neuschreibung des gesamten Codes. Vielmehr wird ein Ansatz empfohlen, der sich auf die Schlüsselelemente (hohe Privilegien, große Angriffsfläche, zentral für Sicherheitsgarantien) konzentriert und neuen Code in einer speichersicheren Sprache schreibt.
  • Die Leistung ist unzureichend.
    • Die Leistung von Rust ist ungefähr vergleichbar mit der von C/C++. Kleine Unterschiede rechtfertigen keine Sicherheitsrisiken. Es gibt auch Situationen, in denen Rust-Programme schneller sein können.
  • Speichersichere Systemprogrammiersprachen wie Rust sind zu neu.
  • Es gibt andere Lösungen wie statische Analyse, Fuzzing und Sandboxing.

Siehe Quantifizierung der Speicherunsicherheit und Reaktionen darauf für eine detaillierte Erörterung der oben genannten Punkte.

Trotz der Einwände wächst die Dynamik für die notwendigen Veränderungen. Große Investitionen werden beispielsweise in die Open Source Software Security Foundation (unterstützt von der Linux Foundation) getätigt. Speichersicherheit wurde in den USA diskutiert Senatsberichte und von der NSAVerbraucherberichte Darüber hinaus arbeitet das Unternehmen daran, die verschiedenen Anreize zu ermitteln, die diese Bewegung beschleunigen könnten, und bietet hierzu eine Reihe von Empfehlungen für Unternehmen und staatliche Stellen an.

Um es zusammenzufassen:

Die Bedeutung der Computer für die Gesellschaft hat in den letzten fünfzig Jahren enorm zugenommen. Auch die Bedrohungslandschaft für unsere Computerinfrastruktur hat sich in den letzten Jahrzehnten radikal verändert. Die Programmiersprachen, die wir zum Aufbau unserer Computersysteme verwenden, haben sich jedoch nicht entsprechend verändert. Mangelnde Speichersicherheit ist die größte Einzelquelle für Sicherheitslücken in Software. Dies ist nicht auf einen bestimmten Softwaretyp beschränkt, denn überall, wo speicherunsichere Sprachen verwendet werden, gibt es zahlreiche Probleme mit der Speichersicherheit. Und den Zahlen nach kommt keine andere Klasse von Verwundbarkeit auch nur annähernd an diese heran.

Wir verfügen nun über die Mittel, um dieses wachsende Problem anzugehen, und es ist von entscheidender Bedeutung, dass wir als Branche dies auch tun. Glücklicherweise setzt sich das Bewusstsein für die Situation immer weiter durch, doch das Problem ist dringend und es gibt keine Zeit zu verlieren.