© ideyweb/Shutterstock.com
Neue Besen kehren gut, sagt man. Aber sind sie auch sicher?

Wasm - Ist das sicher oder kann das weg?


Schon wieder eine neue Technologie für aktive Inhalte – muss das denn wirklich sein? Als hätten wir mit Flash und Java nicht schon genug Ärger gehabt. Beides wurde wahrscheinlich öfter für Angriffe als für wirklich nützliche Anwendungen verwendet.

Jedenfalls wenn man mal von Spielen und in der Websteinzeit dem Wiedergeben von Videos durch Flash absieht. HTML5 und die dazugehörenden JavaScript APIs decken doch schon alle möglichen und unmöglichen Anwendungsfälle ab. Wieso also noch was Neues erfinden? Vor allem, wo doch jede neue Funktionalität immer auch die Angriffsfläche erhöht, die man doch eigentlich möglichst klein halten möchte. Die Antwort ist ganz einfach: Wieso nicht? Vielleicht ist das Neue ja besser als alles Alte.

Wichtig ist natürlich vor allem, dass es sicher ist. Nicht nur sicherer als Flash oder Java – das ist keine Kunst, so chronisch unsicher wie die sind. Sondern wirklich sicher, von Anfang an und ohne Kompromisse. Wobei Java in der Theorie auch sicher sein sollte, nur bei der Umsetzung, vor allem im Browser, hat es dann gewaltig gehakt. So sehr, dass Java im Browser inzwischen im Grunde tot ist.

Wie sieht es also mit der Sicherheit von WebAssembly (auch kurz „Wasm“ genannt) aus? Dazu schauen wir uns erst einmal an, was das überhaupt ist. Und zwar nur im Kontext von Webanwendungen und der Ausführung im Webbrowser, denn das ist der primäre Anwendungszweck. Dass WebAssembly auch in Nicht-Webumgebungen ausgeführt werden kann, ist laut den WebAssembly-Entwicklern zwar wünschenswert, aber kein zentrales Ziel der Entwicklung.

WebAssembly ganz einfach ...

Sehr vereinfacht gesagt ist WebAssembly ein Bytecode, der in einer Sandbox ausgeführt wird. Im Browser werden WebAssembly-Module in der JavaScript VM ausgeführt. Für den WebAssembly-Code gelten daher die gleichen Sicherheitsbeschränkungen wie für JavaScript-Code, insbesondere also die Same-Origin Policy. Hinzu kommt die zusätzliche Einschränkung, dass WebAssembly-Code nicht direkt auf das DOM der Seite zugreifen kann. Für Änderungen an der dargestellten Seite oder das Abfragen von Informationen daraus müssen JavaScript-Funktionen verwendet werden. Auch können die WebAssembly-Module nur dann auf die Hardware oder das Dateisystem zugreifen, wenn der Benutzer das in der Browserkonfiguration explizit erlaubt.

... und etwas formaler

Formal betrachtet ist WebAssembly die Spezifikation einer Instruction Set Architecture und eines Dateiformats. Die Instruction Set Architecture beschreibt einen (virtuellen) Rechner, und das Dateiformat legt den Aufbau von WebAssembly-Modulen fest. Der Browser lädt den WebAssembly-Binärcode in Form von Modulen und führt ihn aus.

Das ähnelt den bekannten Java-Applets, die zu Java-Bytecode kompiliert und in der Java Virtual Machine (VM) ausgeführt werden. Der Unterschied besteht darin, dass Java Applets eine Erweiterung des Browsers sind, die in einem Plug-in ausgeführt werden, während WebAssembly-Bytecode, wie schon erwähnt, im JavaScript-Interpreter der Browser ausgeführt wird.

Entwickelt werden können die WebAssembly-Programme in etlichen Sprachen, angefangen bei C/C++ über Rust und Go bis hin zu C#. Und falls sich irgendwer hinsetzt und einen Compiler dafür schreibt, könnte man auch z. B. Basic C64 verwenden.

Aus Sicherheitssicht ist es aber egal, in welcher Sprache entwickelt wurde. Interessant sind dafür nur der Bytecode und dessen Ausführung. Und eigentlich sogar nur die Ausführung, denn der Bytecode stammt ja aus einer i. Allg. nicht vertrauenswürdigen Quelle, sodass man nicht weiß, ob er harmlos ist oder bösartig, oder vielleicht auch harmlos aber kompromittiert. Sämtliche Schutzmaßnahmen müssen daher im Browser ergriffen werden.

Die Schutzmaßnahmen

Einige der Sprachen, die nach WebAssembly kompiliert werden können, erlauben den Zugriff auf beliebige Speicheradressen. Ein Angreifer könnte das nutzen, um im Browser gespeicherte sicherheitsrelevante Informationen wie z. B. Passwörter oder Authentifizierungstokens auszuspähen, sofern er deren Speicherort kennt. WebAssembly-Modulen werden daher separate Speicherbereiche zugewiesen.

Schutz des Speichers

Speicherzugriffe sind nur auf eine dedizierte Untermenge des Heaps der JavaScript VM möglich. Dieser spezielle Heap wird durch einen JavaScript Array Buffer realisiert. Dadurch gelten für den gesamten Speicher eines WebAssembly-Moduls die gleichen Beschränkungen wie für normale JavaScript-Objekte. Außerdem überwacht dadurch die Garbage Collection die Speichernutzung des Moduls und gibt den Speicher ggf. frei. Speicherlecks sind dadurch nur in den gleichen Grenzen wie bei normalen JavaScript-Programmen möglich.

Da der komplette Heap des WebAssembly-Moduls im ArrayBuffer steckt, weiß die JavaScript-Umgebung, wie groß der Heap des Moduls ist und wo genau er im JavaScript Heap liegt. Dadurch kann sie bei jedem Speicherzugriff des WebAssembly-Codes zum einen exakt nachprüfen, ob sich der Zugriff innerhalb des ArrayBuffers bewegt, und zum anderen unerlaubte Zugriffe unterbinden.

Unerlaubte Speicherzugriffe in die JavaScript VM oder gar in den Adressraum des Browserprozesses werden dadurch effektiv verhindert. Zwar verlangsamen diese Prüfungen die Ausführung des Programms etwas, aber ohne sie wäre die Ausführung von WebAssembly-Code viel zu gefährlich. Denn darin kann potenziell beliebiger Schadcode stecken.

Schutz des Execution-Stacks

Genauso gefährlich wie beliebige Zugriffe auf den Speicher sind Zugriffe auf den Execution-Stack. Der enthält außer den lokalen Variablen auch die Rücksprungadresse an die aufrufende Funktion. Gelingt es bösartigem Code, die Rücksprungadresse zu manipulieren, kann der Angreifer sie nutzen, um beliebigen Code auszuführen. WebAssembly verhindert einen Schreibzugriff auf den Execution-Stack, indem er außerhalb des Speichers des WebAssembly-Moduls gespeichert wird.

Vorsicht bei Sprüngen

Auch Befehle, die an andere Stellen im Code springen, sind generell gefährlich. WebAssembly springt nur bei Funktionsaufrufen Speicheradressen an. Die werden teilweise erst dynamisch zur Laufzeit berechnet. Um Manipulationen zu verhindern, kommen dabei Tabellen zum Einsatz. Statt die Zieladresse direkt im call-Befehl zu speichern, verwenden call-Befehle zwei Parameter: einen Index in einer Tabelle und eine Funktionssignatur. Der Index zeigt in eine Tabelle mit Funktions-Pointern. Die wird genau wie der Execution-Stack außerhalb des WebAssembly-Moduls gespeichert, sodass ein Überschreiben durch das WebAssembly-Modul nicht möglich ist.

Bevor die WebAssembly-Laufzeitumgebung einen call-Befehl ausführt, prüft sie, ob die an den call-Befehl übergebene Funktionssignatur mit dem in der Tabelle am angegebenen Index gespeicherten Wert übereinstimmt. Nur wenn die beiden Signaturen übereinstimmen, wird die Funktion an der in der Tabelle gespeicherten Adresse aufgerufen. Gibt es unter dem Index keinen Eintrag in der Tabe...

Neugierig geworden?

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang