Unsichere Deserialisierung in Node.js verhindern
17. April 2023
0 Min. LesezeitAnmerkung der Redaktion: 31. Mai 2023
Dieser Beitrag wurde aktualisiert und zeigt, wie Snyk Open Source und die Snyk VS Code-Erweiterung Ihnen helfen können, unsichere Deserialisierung in Ihren Projekten zu verhindern.
Serialisierung ist der Prozess, bei dem ein JavaScript-Objekt in einen Stream aufeinanderfolgender Bytes umgewandelt wird, um ihn über ein Netzwerk zu senden oder in einer Datenbank zu speichern. Die Serialisierung ändert das ursprüngliche Datenformat, erhält aber Zustand und Eigenschaften, sodass wir das Objekt bei Bedarf wiederherstellen können. Mit Serialisierung können wir komplexe Daten in Dateien, Datenbanken und im Interprozessspeicher ablegen – und diese komplexen Daten zwischen Komponenten und über Netzwerke hinweg übertragen.
Deserialisierung ist das Gegenteil von Serialisierung: Sie wandelt serialisierte Daten zurück in ein Objekt mit demselben Zustand wie zuvor. Das Problem: Deserialisierung kann unsicher sein und vertrauliche Daten offenlegen. Die Hauptursache für unsichere Deserialisierung ist, dass die Deserialisierung von Benutzereingaben nicht abgesichert wird. Am besten vermeiden Sie dieses Risiko, indem Sie ausschließlich validierte und bereinigte Daten deserialisieren.
Angreifer können diese Schwachstelle ausnutzen, indem sie schädlichen Code in ein scheinbar harmloses serialisiertes Objekt einfügen und es an eine Anwendung senden. Fehlen zusätzliche Sicherheitsprüfungen, deserialisiert die Webanwendung das empfangene Objekt und führt den schädlichen Code aus. Alternativ kann ein Angreifer vertrauliche Daten aus einem serialisierten Objekt auslesen.
Die Folgen unsicherer Serialisierung können schwerwiegend sein. Wenn eine Website Daten unsicher deserialisiert, können Hacker beispielsweise serialisierte Objekte manipulieren und schädliche Daten in den Code einer Anwendung einschleusen. Ein Angreifer kann sogar ein völlig anderes serialisiertes Objekt übergeben, um auf andere Teile der Anwendung zuzugreifen. Dadurch ist die Anwendung weiteren Angriffen und Datenverlusten ausgesetzt.
Unsichere Deserialisierung in Node.js
node-serialize und serialize-to-js sind Node.js- und JavaScript-Pakete, die anfällig für unsichere Deserialisierung sind. Anders als JSON.parse und JSON.stringify, die Objekte nur im JSON-Format serialisieren, serialisieren diese beiden Bibliotheken Objekte nahezu aller Art, einschließlich Funktionen. Diese Eigenschaft macht sie anfällig für Prototype Pollution, einen Injection-Angriff, bei dem sich ein Angreifer Kontrolle über die Standardwerte der Eigenschaften eines Objekts verschafft.
Ist ein Prototype-Pollution-Angriff erfolgreich, kann ein Angreifer weiteren Schaden anrichten, indem er die Anwendungslogik verändert, Remote Code Execution auslöst oder einen Denial-of-Service-Angriff (DoS) durchführt.
Dieser Artikel zeigt, wie Sie Deserialisierungs-Schwachstellen in Node.js beheben. Wir erstellen anfälligen Code, demonstrieren einen Angriff und beheben anschließend die Schwachstellen.
Voraussetzungen
Für dieses Tutorial benötigen Sie:
Node.js installiert
JavaScript-Kenntnisse
Deserialisierungs-Schwachstellen in Node.js beheben
Richten wir zunächst ein Node.js-Projekt ein. Führen Sie den folgenden Befehl aus:
Installieren Sie das Express-Webanwendungs-Framework:
Installieren Sie das Node.js-Serialisierungspaket:
Installieren Sie einen Cookie-Parser:
Erstellen Sie eine neue Datei server.js und fügen Sie den folgenden Code hinzu:
In der obigen Datei verwenden wir das unsichere Modul node-serialize. Außerdem übergeben wir nicht vertrauenswürdige Daten an dessen Funktion unserialize().
Wie viele Sicherheitsprobleme erkennen Sie im obigen Code? Wie würden Sie feststellen, ob der Code oder die hinzugefügten Abhängigkeiten Sicherheitsprobleme aufweisen?
Hier kommt die Snyk Code-Erweiterung ins Spiel. Sie ist kostenlos und schnell einsatzbereit. Gehen Sie in VS Code zu Erweiterungen (SHIFT+CMD+X), suchen Sie nach Snyk, installieren Sie die Erweiterung und schließen Sie sich über 90.000 Entwicklerinnen und Entwicklern an.
Hier sehen Sie einen Screenshot von Snyk Code in Aktion mit dem zuvor gezeigten Code-Snippet der Express-Anwendung:

Beachten Sie die violetten Pfeile und wie sie auf potenzielle Sicherheitsprobleme in diesem Express-Anwendungsserver hinweisen. Wenn Sie genau hinsehen, erkennen Sie weitere Probleme. Außerdem sehen Sie, wie Snyk Code die Codezeilen in der Datei `server.js` kommentiert und veranschaulicht, wie unsicherer Code zwischen verschiedenen Codepfaden und Codedateien fließt.
Holen Sie sich die Snyk VS Code-Erweiterung – sie ist kostenlos!
Zurück zur Node.js-Webanwendung: Der Endpunkt app.get('/', function(req, res) ist anfällig für unsichere Deserialisierung. Denn die Anwendung fügt Benutzereingaben einem bereits serialisierten Wert eines Einstellungs-Cookies hinzu.
Um die Schwachstelle zu demonstrieren, starten Sie den Express-Server mit diesem Befehl:
Dadurch wird der Server auf Port 3000 gestartet. Öffnen Sie http://localhost:3000/, um die Seite aufzurufen und die Cookies wie unten gezeigt zu überprüfen:

Wandeln Sie das Cookie nun vom Base64-Format in das JSON-Format um. Dafür können Sie ein Online-Tool wie Code Beautify verwenden. Dies ist das Cookie in Base64:
Und so sieht das Cookie aus, das der Server nach der Umwandlung von Base64 in JSON setzt:
Um das Deserialisierungsproblem in Aktion zu sehen, ändern wir den Wert des JSON-Objekts und kodieren ihn in Base64. Anschließend ersetzen wir den Cookie-Wert im Browser durch das neue Cookie.
Ändern Sie das JSON-Objekt wie folgt:
Öffnen Sie dieses Base64-Tool, um das JSON zu kodieren. In Base64 kodiert ergibt sich:
Ersetzen Sie nun den aktuellen Cookie-Wert im Browser durch den kodierten Wert des neuen Objekts und ändern Sie den Wert der HTTP-Antwort in der Datei server.js wie folgt:
Wenn Sie die Verbindung neu starten und die Seite aktualisieren, sehen Sie die neue Antwort.
Die Schwachstelle ausnutzen
Nutzen wir die Schwachstelle aus, um beliebige Codeausführung (ACE) durchzuführen, bei der nicht vertrauenswürdige Daten an die Funktion unserialize() übergeben werden.
Zunächst erstellen wir mit der Funktion serialize() im selben Modul, mit dem wir gearbeitet haben, eine Payload. Erstellen Sie dazu eine Datei namens serialize.js und fügen Sie den folgenden Code hinzu:
Dieses Node.js-Skript serialisiert unseren Code.
Öffnen Sie jetzt ein Terminal, wechseln Sie zum Stammordner und führen Sie diesen Befehl aus:
Ersetzen Sie den Parameter username im Cookie-Wert durch den Wert des Parameters myOutput in der obigen Ausgabe. Unser neues Objekt im Cookie sollte nun so aussehen:
Die obige Ausgabe liefert uns eine serialisierte Payload, die wir an die Funktion unserialize() übergeben. Um ACE zu erreichen, müssen wir einen sofort aufgerufenen JavaScript-Funktionsausdruck (Immediately Invoked Function Expression, IIFE) verwenden, um die Funktion aufzurufen.
Fügen wir die IIFE-Klammern () nach dem Funktionskörper dieser serialisierten Payload ein, wird die Funktion nach der Objekterstellung ausgeführt. Unten sehen Sie das JSON-Objekt, das nun im Cookie gespeichert wird.
Um zu zeigen, wie dieses manipulierte Objekt eine dynamische Codeauswertung auf einem laufenden Node.js-Server ermöglicht, verwenden wir es als Beispiel in unserer Hilfsdatei serialize.js. Übergeben wir diese Payload an eine Funktion unserialize(). Ändern Sie die Datei seralize.js, indem Sie den folgenden Code hinzufügen:
Im obigen Code haben wir eine neue Variable km hinzugefügt. Ihr Wert ist eine Funktion, die wir als Wert von username an die Funktion unserialize() übergeben haben.
Führen Sie nun die Skriptdatei aus, um mit dem folgenden Befehl zu prüfen, ob die Funktion ausgeführt wird:
Sie sollten folgende Ausgabe erhalten:

Der Serialisierungsprozess wurde erfolgreich ausgenutzt! Als Angreifer, die die Eingabe des clientseitigen Objekts kontrollieren, konnten wir es so manipulieren, dass es JavaScript-Code enthält, der das Feld für den Benutzernamen ändert.
Die Schwachstelle durch unsichere Deserialisierung beheben
Am besten verhindern Sie unsichere Deserialisierung, indem Sie Benutzereingaben gar nicht erst deserialisieren. Die zweitbeste Möglichkeit ist, die Benutzereingabe vor der Serialisierung zu überprüfen. Mit dem Paket Serialize JavaScript können wir die Eingaben im anfälligen Beispiel oben bereinigen.
Installieren Sie zunächst serialize-javaScript mit npm:
Ändern Sie die Datei serialize.js, indem Sie den Code durch Folgendes ersetzen:
ignoreFunction stellt sicher, dass die Funktionen, mit denen ACE ausgeführt wird, nicht serialisiert werden. Führen Sie die Datei aus, um dies zu überprüfen:
Hier ist die Ausgabe:

Stellen wir sicher, dass das in diesem Artikel empfohlene neue serialize-javascript laut CVE-Berichten tatsächlich frei von Schwachstellen ist. Im folgenden Befehl npm ls sehen Sie, dass es neben dem anfälligen Paket node-serialize zum Projekt hinzugefügt wurde:
Anschließend scannen wir es mit dem kostenlosen npm-Paket snyk:
Das zeigt, dass das npm-Paket node-serialize anfällig ist, wie wir in diesem Artikel demonstriert haben. Außerdem sind für das empfohlene npm-Paket serialize-javascript keine öffentlich bekannten Sicherheitslücken verzeichnet.
Wir empfehlen Ihnen dringend, mit Snyk Open Source zu starten und das npm-Paket snyk in Ihren routinemäßigen Entwicklungs-Workflow einzubinden. Integrieren Sie es auch in Ihre CI/CD-Pipeline, damit keine neuen Sicherheitslücken entstehen.
Best Practices zur Vermeidung von Deserialisierungs-Schwachstellen
Die Deserialisierung von Benutzereingaben kann Angreifern ermöglichen, ein System anzugreifen und vertrauliche Daten offenzulegen. Am besten schützen Sie sich vor unsicherer Deserialisierung, indem Sie Daten aus nicht vertrauenswürdigen Quellen nicht deserialisieren. Wenn eine Deserialisierung erforderlich ist, sollten Sie zusätzliche Sicherheitsmaßnahmen implementieren, um sicherzustellen, dass die Daten nicht manipuliert wurden.
Außerdem sollten Sie Eingaben bereinigen. Dazu können Sie prüfen, ob das zu serialisierende Objekt Funktionen enthält, und die Serialisierung gegebenenfalls abbrechen.
Sichere Deserialisierung
Serialisierung und Deserialisierung sind wichtige Prozesse, die eine nahtlose Datenübertragung über ein Netzwerk ermöglichen. Unsichere Deserialisierung kann jedoch zu schwerwiegenden Schwachstellen führen. Indem Angreifer schädliche serialisierte Objekte in eine Web-App einschleusen – in der Regel als Benutzereingaben, die anschließend deserialisiert werden –, können sie einer Anwendung schädliche Daten übergeben. Daraufhin können sie einen Injection-Angriff, Remote Code Execution oder einen DDoS-Angriff ausführen.
Angriffe über diese Schwachstelle lassen sich verhindern, indem Sie Benutzereingaben nicht deserialisieren. Ist eine Deserialisierung unvermeidbar, sollten Sie zusätzliche Sicherheitsmechanismen wie Anti-Forgery-Tokens einsetzen, um sicherzustellen, dass die Daten nicht verändert wurden.
Besuchen Sie den Snyk-Blog, um mehr darüber zu erfahren, wie Sie Schwachstellen durch unsichere Serialisierung und Deserialisierung in Java verhindern.
Um Ihren Code vor Schwachstellen durch unsichere Serialisierung und Deserialisierung zu schützen, fügen Sie das Snyk.io-Plugin zu Ihrer IDE hinzu.
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.



