Sicherheitsbedenken bei einer JavaScript-Sandbox mit dem Node.js-VM-Modul
22. Februar 2023
0 Min. LesezeitWurden Sie damit beauftragt, ein Produkt zu entwickeln, das dynamisches JavaScript ausführt, das von Endnutzern stammt? Vielleicht denken Sie, dass sich mit dem Node.js-VM-Modul eine JavaScript-Sandbox erstellen lässt. In diesem Artikel erfahren Sie, warum das keineswegs empfehlenswert ist und welche Sicherheitsrisiken damit verbunden sind.
Von Zeit zu Zeit gibt es Projekte, die die routinemäßige Backend-Entwicklung auf die Probe stellen. APIs? Message Queues? Hoher Verarbeitungs- und Rechenaufwand? Nein. Hier ist eine Backlog-Story, über die Sie nachdenken können:
Das ist etwas vage, denn es geht allgemein um die Ausführung nicht vertrauenswürdigen Codes von Nutzern. Es gibt jedoch konkrete Beispiele aus der Praxis, bei denen dies erforderlich sein kann:
Das Produkt replit ermöglicht es Ihnen, in der Cloud zu programmieren und Code in einer benutzerdefinierten IDE auszuführen
Verschiedene Plattformen zum Üben und für Vorstellungsgespräche rund um „Leet-Code“ ermöglichen es Ihnen, eigenen Code zu schreiben, etwa um einen vorgegebenen Algorithmus zu implementieren oder Tests dafür zu verfassen.
Hier kommt das Node.js-VM-Modul ins Spiel.
Das Node.js-VM-Modul
Mit dem Kernmodul node:vm können Entwickler dynamisch bereitgestellten Code in V8-Virtual-Machine-Kontexten kompilieren und ausführen. Zunächst denken Sie dabei vielleicht an die JavaScript-Funktion eval, die als Sicherheitsrisiko bekannt ist. Ist das Node.js-VM-Modul sicherer? Schließlich heißt es „virtuelle Maschine“.
Sehen wir uns ein praktisches Beispiel an:
Im obigen Codebeispiel wird benutzergenerierter Eingabecode mit benutzerdefiniertem JavaScript (gekennzeichnet durch die Variable userInputCustomJavaScriptCode) gezielt in einem Kontext mit bestimmten Variablen ausgeführt: vm.runInContext(). Das Codebeispiel wird erfolgreich ausgeführt und gibt die folgenden Ergebnisse aus:
Angreifer können diese Art von dynamischem JavaScript-Code jedoch missbrauchen, indem sie andere Variablen als die ursprünglich zugewiesenen manipulieren. In diesem konstruierten Node.js-Beispiel legt eine Variable namens productExpirationDays die Ablaufzeit für den Nutzer fest. Was wäre, wenn ein Angreifer den Inhalt seines dynamischen JavaScript-Codes so änderte, dass dieser Wert höher gesetzt wird?
Wenn wir das vorherige Beispiel mit Nutzereingaben durch dieses ersetzen würden, das die Ablaufzeit verlängern soll, erhielten wir die folgende Fehlermeldung:
Der Fehler tritt auf, weil im Gültigkeitsbereich des Kontexts für die virtuelle Maschine, in der der dynamische JavaScript-Code ausgeführt wird, keine Variable productExpirationDays definiert ist oder existiert.
Alles in allem scheint es, als hätten wir einen Weg gefunden, JavaScript-Code sicher und dynamisch in einer abgeschotteten JavaScript-Sandbox auszuführen.
Eine unsichere JavaScript-Sandbox
Das Codebeispiel, das die Methoden createContext() und vm.runInContext() des Node.js-VM-Moduls verwendet, war stark vereinfacht. In der Realität nutzen schädliche Nutzereingaben jedoch oft intelligentere, kreativere und effektivere Methoden, um aus der JavaScript-Sandbox auszubrechen.
Sehen wir uns an, wie ein Angreifer unsicheren Code einschleusen kann, der einen Denial-of-Service-Angriff auf eine laufende Anwendung auslöst. Betrachten Sie das folgende Node.js-Skript:
Im obigen Codebeispiel hat der Nutzer seiner Eingabe eine Endlosschleife hinzugefügt: while(true) {}. Der Code verändert zwar keine anderen Anwendungsvariablen, löst aber einen Denial-of-Service-Angriff aus.
Die Risiken einer unsicheren JavaScript-Sandbox umfassen auch die Remote-Codeausführung. Mit this.constructor.constructor können wir auf das JavaScript-Objekt Function verweisen. Eine JavaScript-Funktion kann Code als String entgegennehmen und anschließend ausführen. Unser Angriff nutzt diese Eigenschaft aus und gibt mithilfe einer sofort aufgerufenen Funktionsausführung, kurz IIFE, die Umgebungsvariablen des laufenden Node.js-Prozesses aus:
Bei der Ausführung des obigen Codebeispiels wird die Ausgabe von process.env angezeigt, in der alle Umgebungsvariablen aufgeführt sind.
An diesem Punkt sollten Sie erkennen, welche Folgen Remote-Codeausführung haben kann, wenn nicht vertrauenswürdiger Code im VM-Modul von Node.js ausgeführt wird. Wenn Nutzer eigenen Code ausführen dürfen, haben sie vollständigen Zugriff auf die Node.js-Serverlaufzeit und können Prozesse starten, auf das Dateisystem zugreifen und vieles mehr.
Zusammenfassung
Die Folgen einer unsicheren JavaScript-Sandbox in einer Node.js-Umgebung sind schwerwiegend und können eine ganze Anwendung lahmlegen. Wir haben gesehen, wie ein Angreifer die JavaScript-Sandbox missbrauchen und schädliche Eingaben bereitstellen kann, die eine Node.js-Anwendung beeinträchtigen und eine Denial-of-Service-Schwachstelle verursachen. Die Angriffsfläche beschränkt sich jedoch nicht darauf. Auch Remote-Codeausführung ist möglich. Dadurch können Angreifer eigenen Code in Node.js-Serverumgebungen ausführen und so die gesamte Anwendungsplattform gefährden.
Tatsächlich ist die durch eine JavaScript-Sandbox mit dem Node.js-VM-Modul entstehende Sicherheitslücke nur der Anfang eines größeren drohenden Datendiebstahls. Sobald ein einzelner Node.js-Anwendungsserver kompromittiert ist, können vertrauliche Informationen offengelegt werden, etwa Zugangsdaten für eine Datenbank oder Cloud-Dienste. Diese könnten es einem Angreifer ermöglichen, den Angriff auszuweiten und sich lateral im bereitgestellten Netzwerk zu bewegen.
Daher empfiehlt es sich, das VM-Modul von Node.js nicht als sichere Sandbox für die Ausführung nicht vertrauenswürdigen JavaScript-Codes zu verwenden. Darauf wird in der Node.js-API-Dokumentation ausdrücklich hingewiesen: „Das Modul node:vm ist kein Sicherheitsmechanismus. Verwenden Sie es nicht, um nicht vertrauenswürdigen Code auszuführen.“
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.
