Am 3. Januar 2018
enthüllten Google Project Zero und andere
die ersten drei einer neuen Klasse von Sicherheitslücken, die spekulative Ausführungsprozessoren betreffen. Sie hießen
Spectre (1 und 2) und
Meltdown . Mithilfe
spekulativer CPU-
Ausführungsmechanismen kann ein Angreifer vorübergehend sowohl explizite als auch implizite Software-Sicherheitsüberprüfungen umgehen, die verhindern, dass Programme unzugängliche Daten im Speicher lesen. Während die spekulative Ausführung als Teil der auf Architekturebene unsichtbaren Mikroarchitektur entworfen wurde, konnten sorgfältig entworfene Programme unzugängliche Informationen in einem spekulativen Block lesen und über Seitenkanäle wie die Ausführungszeit eines Programmfragments anzeigen.
Als gezeigt wurde, dass Spectre-Angriffe mit JavaScript möglich sind, beteiligte sich das V8-Team an der Lösung des Problems. Wir haben ein Notfallteam gebildet und eng mit anderen Google-Teams, unseren anderen Browserpartnern und Hardwarepartnern zusammengearbeitet. Zusammen mit ihnen führten wir proaktiv sowohl offensive Forschung (Aufbau von Angriffsmodulen zum Nachweis des Konzepts) als auch Defensive (Minderung potenzieller Angriffe) durch.
Der Spectre-Angriff besteht aus zwei Teilen:
- Verlust sonst unzugänglicher Daten in den latenten Zustand der CPU . Alle bekannten Spectre-Angriffe verwenden Spekulationen, um Bits unzugänglicher Daten an CPU-Caches zu übertragen.
- Abrufen eines verborgenen Status zum Wiederherstellen unzugänglicher Daten. Dafür benötigt ein Angreifer eine Uhr mit ausreichender Genauigkeit. (Überraschend geringe Genauigkeit, insbesondere bei Methoden wie Kantenschwellenwert - Vergleich mit einem Schwellenwert entlang eines ausgewählten Umrisses).
Theoretisch würde es ausreichen, eine der beiden Komponenten des Angriffs zu blockieren. Da wir nicht wissen, wie wir sie vollständig blockieren können, haben wir Schadensbegrenzungen entwickelt und implementiert, die die Menge an Informationen, die in CPU-Caches gelangen, und Schadensbegrenzungen, die die Wiederherstellung eines verborgenen Zustands erschweren, erheblich reduzieren.
Hochpräzise Timer
Winzige Zustandsänderungen, die nach spekulativer Ausführung bestehen bleiben, erzeugen entsprechend winzige, fast unmöglich winzige zeitliche Unterschiede - in der Größenordnung einer Milliardstel Sekunde. Um einzelne solche Unterschiede direkt zu erkennen, benötigt der Angreifer einen hochpräzisen Timer. Prozessoren bieten solche Timer an, aber die Webplattform setzt sie nicht. Der genaueste Timer auf der Webplattform
performance.now()
hatte eine Auflösung von mehreren Mikrosekunden, was für diesen Zweck zunächst als ungeeignet angesehen wurde. Vor zwei Jahren veröffentlichte
eine auf mikroarchitektonische Angriffe spezialisierte Forschungsgruppe
einen Artikel über Timer auf einer Webplattform. Sie kamen zu dem Schluss, dass gleichzeitig veränderbarer gemeinsamer Speicher und verschiedene Auflösungswiederherstellungsmethoden die Erstellung von Timern mit noch höherer Auflösung bis zur Nanosekunde ermöglichen. Solche Timer sind genau genug, um einzelne Treffer und Fehlschläge des L1-Cache zu erkennen. Er wird normalerweise verwendet, um Informationen bei Spectre-Angriffen zu erfassen.
Timer-Schutz
Um die Fähigkeit zu stören, kleine Zeitunterschiede zu erkennen, haben Browserentwickler einen multilateralen Ansatz gewählt. In allen Browsern wurde die Auflösung von
performance.now()
reduziert (in Chrome von 5 Mikrosekunden auf 100) und zufälliger Jitter eingeführt, um die Wiederherstellung der Auflösung zu verhindern. Nach Konsultationen zwischen den Entwicklern aller Browser haben wir gemeinsam beschlossen, einen beispiellosen Schritt zu unternehmen: Deaktivieren Sie die
SharedArrayBuffer
API sofort und rückwirkend in allen Browsern, um die Erstellung eines Nanosekunden-Timers zu verhindern.
Gewinn
Zu Beginn unserer offensiven Forschung wurde klar, dass Timer-Abschwächungen allein nicht ausreichen. Einer der Gründe ist, dass ein Angreifer seinen Code einfach wiederholt ausführen kann, sodass der kumulative Zeitunterschied viel mehr als ein Treffer oder ein Cache-Fehler beträgt. Wir konnten zuverlässige "Gadgets" erstellen, die viele Cache-Zeilen gleichzeitig verwenden, bis zur gesamten Cache-Kapazität, was einen Zeitunterschied von bis zu 600 Mikrosekunden ergibt. Später entdeckten wir beliebige Verstärkungsmethoden, die nicht durch die Cache-Kapazität begrenzt sind. Solche Amplifikationsverfahren basieren auf wiederholten Versuchen, geheime Daten zu lesen.
JIT-Schutz
Um unzugängliche Daten mit Spectre zu lesen, zwingt ein Angreifer die CPU, spekulativ Code auszuführen, der normalerweise unzugängliche Daten liest und in den Cache legt. Schutz kann von zwei Seiten betrachtet werden:
- Verhindern Sie die Ausführung spekulativer Codes.
- Verhinderung des Lesens unzugänglicher Daten aus der spekulativen Pipeline.
Wir haben mit der ersten Option experimentiert, indem wir empfohlene Anweisungen
LFENCE
, um Spekulationen zu verhindern, z. B. Intel
LFENCE
aus jedem kritischen bedingten Zweig, und
Retpoline für indirekte Zweige verwendet haben. Leider verringern solche starken Abschwächungen die Produktivität erheblich (2-3-fache Verlangsamung des Octane-Benchmarks). Stattdessen haben wir den zweiten Ansatz gewählt, indem wir Schadensbegrenzungssequenzen eingefügt haben, die verhindern, dass sensible Daten aufgrund unangemessener Spekulationen gelesen werden. Lassen Sie mich die Technik mit dem folgenden Codeausschnitt veranschaulichen:
if (condition) { return a[i]; }
Der Einfachheit halber nehmen wir die Bedingung
0
oder
1
. Der obige Code ist anfällig, wenn die CPU spekulativ aus
a[i]
liest, wenn
i
außerhalb des Bereichs liegt, und Zugriff auf normalerweise nicht zugängliche Daten erhält. Eine wichtige Beobachtung ist, dass in diesem Fall die Spekulation versucht,
a[i]
zu lesen, wenn die Bedingung
0
. Unsere Schadensbegrenzung schreibt dieses Programm so um, dass es sich genauso verhält wie das ursprüngliche Programm, jedoch keine spekulativ geladenen Daten auslaufen lässt.
Wir reservieren ein CPU-Register, das wir "Gift" nennen, um zu verfolgen, ob Code in einem falsch interpretierten Zweig ausgeführt wird. Das Giftregister wird in allen Zweigen und Aufrufen des generierten Codes unterstützt, sodass jeder falsch interpretierte Zweig dazu führt, dass das Giftregister
0
. Dann messen wir alle Speicherzugriffe so, dass sie das Ergebnis aller Downloads bedingungslos mit dem aktuellen Wert des Giftregisters maskieren. Dies hindert den Prozessor nicht daran, die Zweige vorherzusagen (oder falsch zu interpretieren), zerstört jedoch die Informationen (möglicherweise außerhalb der Grenzen) der geladenen Werte aufgrund falsch interpretierter Zweige. Der Werkzeugcode wird unten angezeigt (
a
ist ein Array von Zahlen).
let poison = 1;
Zusätzlicher Code hat keinen Einfluss auf das normale (von der Architektur definierte) Verhalten des Programms. Dies wirkt sich nur auf den Status der Mikroarchitektur aus, wenn an einer CPU mit spekulativer Ausführung gearbeitet wird. Wenn Sie ein Programm auf Quellcode-Ebene instrumentieren, können erweiterte Optimierungen in modernen Compilern solche Instrumente entfernen. In V8 verhindern wir, dass der Compiler Schadensbegrenzungen entfernt, indem wir sie zu einem sehr späten Zeitpunkt der Kompilierung einfügen.
Wir verwenden diese Vergiftungstechnik auch, um Lecks von indirekten Verzweigungen in der Bytecode-Schleife des Interpreters und in der Folge von JavaScript-Funktionsaufrufen zu verhindern. Im Interpreter setzen wir das Gift auf
0
wenn der Bytecode-Handler (d. H. Eine Maschinencodesequenz, die einen Bytecode interpretiert) nicht mit dem aktuellen Bytecode übereinstimmt. Bei JavaScript-Aufrufen übergeben wir die Zielfunktion als Parameter (im Register) und setzen das Gift zu Beginn jeder Funktion auf
0
wenn die eingehende Zielfunktion nicht mit der aktuellen Funktion übereinstimmt. Mit dieser Abschwächung sehen wir eine Verlangsamung der Octane-Benchmark um weniger als 20%.
Die Schadensbegrenzung für WebAssembly ist einfacher, da die Hauptsicherheitsprüfung darin besteht, sicherzustellen, dass der Speicherzugriff innerhalb der Grenzen liegt. Bei 32-Bit-Plattformen füllen wir zusätzlich zu den üblichen Grenzwertprüfungen den gesamten Speicher bis zur nächsten Zweierpotenz und maskieren bedingungslos alle oberen Bits des Benutzerspeicherindex. 64-Bit-Plattformen benötigen keine solche Abschwächung, da die Implementierung den Schutz des virtuellen Speichers für Grenzkontrollen verwendet. Wir haben mit dem Kompilieren von switch / case-Anweisungen in binären Suchcode experimentiert, anstatt einen potenziell anfälligen indirekten Zweig zu verwenden, der jedoch für einige Workloads zu teuer ist. Indirekte Aufrufe sind durch Retpolins geschützt.
Softwareschutz - unzuverlässig
Glücklicherweise oder unglücklicherweise ging unsere offensive Forschung viel schneller voran als die defensive, und wir fanden es schnell unmöglich, alle möglichen Lecks während Spectre-Angriffen programmgesteuert abzumildern. Dafür gibt es mehrere Gründe. Erstens sind die technischen Anstrengungen zur Bekämpfung von Spectre in keinem Verhältnis zum Ausmaß der Bedrohung. In V8 treten viele andere Sicherheitsrisiken auf, die viel schlimmer sind, z. B. das direkte Lesen außerhalb der Grenzen aufgrund häufiger Fehler (die schneller und einfacher als Spectre sind), das Schreiben außerhalb der Grenzen (dies ist mit Spectre und schlechter nicht möglich) und potenzielle Remote-Risiken Codeausführung (unmöglich mit Spectre und viel, viel schlimmer). Zweitens waren die von uns entwickelten und implementierten immer ausgefeilteren Maßnahmen zur Schadensbegrenzung mit einer erheblichen Komplexität verbunden, die eine technische Verpflichtung darstellt und die Angriffsfläche und den Leistungsaufwand tatsächlich erhöhen kann. Drittens ist das Testen und Aufrechterhalten der Minderung von Mikroarchitekturlecks noch schwieriger als das Entwerfen der Geräte selbst für einen Angriff, da es schwierig ist, sicherzustellen, dass die Minderung weiterhin so funktioniert, wie sie entworfen wurde. Mindestens einmal wurden wichtige Abhilfemaßnahmen durch spätere Compileroptimierungen effektiv rückgängig gemacht. Viertens haben wir festgestellt, dass eine effektive Reduzierung einiger Spectre-Optionen, insbesondere von Option 4, in der Software einfach nicht möglich ist, selbst nach den heldenhaften Bemühungen unserer Apple-Partner, das Problem in ihrem JIT-Compiler zu lösen.
Standortisolierung
Unsere Forschung führte zu dem Schluss: Im Prinzip kann nicht vertrauenswürdiger Code den gesamten Adressraum eines Prozesses mithilfe von Spectre und Seitenkanälen lesen. Software-Minderungen verringern die Effektivität vieler potenzieller Gadgets, sind jedoch nicht effektiv oder umfassend. Die einzig wirksame Maßnahme besteht darin, vertrauliche Daten außerhalb des Prozessadressraums zu verschieben. Glücklicherweise versucht Chrome seit vielen Jahren, Websites in verschiedene Prozesse zu unterteilen, um die Angriffsfläche aufgrund allgemeiner Sicherheitslücken zu verringern. Diese Investitionen haben sich ausgezahlt, und bis Mai 2018 haben wir die Bereitschaft erreicht und die
Isolation von Standorten auf die maximale Anzahl von Plattformen ausgeweitet. Daher übernimmt das Chrome-Sicherheitsmodell während des Rendervorgangs keinen Datenschutz mehr.
Spectre hat einen langen Weg zurückgelegt und die Vorzüge der Zusammenarbeit von Entwicklern in Industrie und Wissenschaft hervorgehoben. Bisher sind weiße Hüte vor schwarzen. Wir wissen immer noch nichts über einen einzigen echten Angriff, mit Ausnahme von neugierigen Experimentatoren und professionellen Forschern, die Geräte entwickeln, um das Konzept zu beweisen. Es werden weiterhin neue Varianten dieser Sicherheitsanfälligkeiten angezeigt, die noch einige Zeit andauern werden. Wir überwachen diese Bedrohungen weiterhin und nehmen sie ernst.
Wie viele Programmierer dachten wir auch, dass sichere Sprachen die richtige Grenze für die Abstraktion darstellen und verhindern, dass gut typisierte Programme beliebigen Speicher lesen. Es ist traurig, dass sich dies als Fehler herausstellte - diese Garantie entspricht nicht der heutigen Ausrüstung. Natürlich glauben wir immer noch, dass sichere Sprachen mehr technische Vorteile haben und die Zukunft bei ihnen liegt, aber ... auf den heutigen Geräten lecken sie ein wenig.
Interessierte Leser können sich eingehender mit dem Thema befassen und detailliertere Informationen in unserem
wissenschaftlichen Artikel erhalten .