Abwehr von Node.js-Webanwendungen gegen Prototype-Pollution- und Request-Smuggling-Angriffe
Grace Collins
Solutions Engineer · Leapcell

Einleitung
In der sich rasant entwickelnden Landschaft der Webentwicklung hat sich Node.js als Eckpfeiler für den Aufbau skalierbarer und hochleistungsfähiger serverseitiger Anwendungen etabliert. Seine asynchrone, ereignisgesteuerte Architektur und das riesige Ökosystem von Bibliotheken haben es zu einer beliebten Wahl gemacht. Große Macht bringt jedoch große Verantwortung mit sich, und die zunehmende Komplexität von Webanwendungen bringt auch ausgefeilte Sicherheitsbedrohungen mit sich. Unter diesen stechen Prototype Pollution und Request Smuggling als besonders heimtückische Schwachstellen hervor, die die Integrität und Verfügbarkeit von Node.js-Webdiensten gefährden können. Dieser Artikel befasst sich mit den Mechanismen dieser Angriffe, veranschaulicht deren potenzielle Auswirkungen und bietet umsetzbare Strategien zur Stärkung Ihrer Node.js-Anwendungen dagegen. Das Verständnis dieser Bedrohungen und die Implementierung robuster Abwehrmachanismen sind nicht nur gute Praxis; sie sind unerlässlich, um sensible Daten zu schützen und das Vertrauen der Benutzer in der heutigen vernetzten digitalen Welt zu wahren.
Verstehen der Bedrohungen
Bevor wir uns mit Abwehrstrategien befassen, wollen wir ein klares Verständnis der Kernkonzepte im Zusammenhang mit diesen Angriffen aufbauen.
Kernterminologie
- Prototype Chain (Prototypenkette): In JavaScript können Objekte Eigenschaften und Methoden von anderen Objekten erben. Diese Vererbung wird durch eine Prototypenkette erreicht. Jedes JavaScript-Objekt verfügt über eine interne Eigenschaft
[[Prototype]]
(oder__proto__
in vielen Umgebungen), die auf sein Prototypobjekt verweist. Wenn Sie versuchen, auf eine Eigenschaft eines Objekts zuzugreifen, und diese nicht direkt auf dem Objekt gefunden wird, sucht JavaScript danach im Prototyp des Objekts, dann im Prototyp des Prototyps usw., bis es beiObject.prototype
(der Wurzel fast aller Prototypketten) ankommt oder die Eigenschaft gefunden wird. - Prototype Pollution (Prototypvergiftung): Diese Schwachstelle ermöglicht es einem Angreifer, Eigenschaften des Prototyps eines Objekts (normalerweise
Object.prototype
) einzufügen oder zu ändern. Da fast alle Objekte vonObject.prototype
erben, kann deren Vergiftung beliebige Objekte in der gesamten Anwendung beeinträchtigen, einschließlich derjenigen, die indirekt vom Framework oder anderen Bibliotheken erstellt wurden. - Request Smuggling (Anforderungsschmuggel): Dies ist eine Technik, bei der ein Angreifer Lücken in der Art und Weise ausnutzt, wie verschiedene Proxys, Load Balancer oder Webserver die Grenzen einer HTTP-Anfrage interpretieren. Indem ein Angreifer eine präparierte Anfrage sendet, die für den Front-End-Server als eine Anfrage erscheint, aber für den Back-End-Server als zwei oder mehr Anfragen, kann er Sicherheitskontrollen umgehen, unbefugte Ressourcen abrufen oder Caches vergiften.
- HTTP/1.1
Content-Length
-Header: Gibt die Größe des Nachrichtenkörpers in Oktetten (8-Bit-Bytes) an. - HTTP/1.1
Transfer-Encoding
-Header: Gibt die für den Nachrichtenkörper angewendete Verschlüsselung an, um eine sichere Übertragung zu gewährleisten. Der gebräuchlichste Wert istchunked
, was bedeutet, dass der Nachrichtenkörper aus mehreren Chunks besteht.
Prototype Pollution: Mechanismen und Auswirkungen
Prototype Pollution nutzt die dynamische Natur von JavaScript-Objekten und der Prototypenkette aus. Die Schwachstelle entsteht typischerweise, wenn JavaScript-Funktionen Objekte rekursiv zusammenführen oder JSON-Daten verarbeiten, ohne die Eingabeschlüssel angemessen zu validieren, was es einem Angreifer ermöglicht, __proto__
als Schlüssel in benutzergesteuerten Daten einzufügen. Wenn solche Daten in ein anderes Objekt zusammengeführt werden und die Zusammenführungslogik Eigenschaften direkt zuweist, ohne zu prüfen, ob der Schlüssel __proto__
ist, kann dies unbeabsichtigt Object.prototype
ändern.
Betrachten Sie ein häufiges Szenario, in dem eine Hilfsfunktion eine Standardkonfiguration mit benutzerspezifischen Einstellungen zusammenführt:
// Eine vereinfachte, anfällige Merge-Funktion function merge(target, source) { for (const key in source) { if (key === '__proto__' || key === 'constructor') { // Grundlegende Prüfung, aber oft übersehen oder unzureichend continue; } if (target[key] instanceof Object && source[key] instanceof Object) { merge(target[key], source[key]); // Rekursives Zusammenführen } else { target[key] = source[key]; } } return target; } const defaultConfig = { env: 'production', db: { host: 'localhost' } }; // Angreifergesteuerte Eingabe const maliciousInput = JSON.parse('{"__proto__": {"isAdmin": true}}'); // Wenn die Merge-Funktion den Schlüssel nicht richtig bereinigt, passiert Folgendes: // merge(defaultConfig, maliciousInput) würde in diesem einfachen Beispiel Object.prototype nicht direkt vergiften // Wenn die Merge-Funktion jedoch target[key] = source[key] direkt verwendet, wobei target ein Proxy zu Object.prototype ist // oder wenn tief verschachteltes Merging involviert ist, wird es zu einem Problem. // Lassen Sie uns ein direkteres Vergiftungsbeispiel veranschaulichen const vulnerableMerge = (target, source) => { for (const key in source) { if (key === '__proto__') { Object.assign(target, source); // Direkte Zuweisung zur Demonstration, reale Szenarien sind komplexer } else if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { if (!target[key] || typeof target[key] !== 'object') { target[key] = {}; } vulnerableMerge(target[key], source[key]); // Rekursiver Aufruf } else { target[key] = source[key]; } } }; // Beispiel für Vergiftung const userControlledObject = {}; vulnerableMerge(userControlledObject, JSON.parse('{"__proto__": {"isAdmin": true}}')); // Jetzt könnte jedes neu erstellte Objekt diese Eigenschaft erben const newUser = {}; console.log(newUser.isAdmin); // true (Oh weh!)
Die Auswirkungen von Prototype Pollution können schwerwiegend sein und reichen von Denial of Service (durch Abstürzen von Anwendungen), Eskalation von Berechtigungen (durch Einfügen von Administrator-Flaggen) bis hin zur Remote Code Execution (RCE) durch Manipulation von Framework-Interna, die auf bestimmten Prototypeneigenschaften basieren (z. B. Konfigurationen von Template-Engines).
Abwehrmaßnahmen gegen Prototype Pollution
-
Eingabevalidierung und Bereinigung: Die effektivste Abwehr ist die sorgfältige Validierung und Bereinigung aller benutzertkontrollierten Eingaben, insbesondere wenn sie Objektzusammenführung oder Deserialisierung beinhalten. Verhindern Sie
__proto__
undconstructor
als Schlüssel.function safeMerge(target, source) { for (const key in source) { // Explizit __proto__ und constructor.prototype verbieten if (key === '__proto__' || key === 'constructor') { continue; } if (target[key] instanceof Object && source[key] instanceof Object) { // Sicherstellen, dass target[key] selbst ein Plain-Objekt ist, um das Vergiften von eingebauten Prototypen zu vermeiden if (Object.getPrototypeOf(target[key]) === Object.prototype) { safeMerge(target[key], source[key]); } else { target[key] = source[key]; // Oder als Fehler behandeln } } else { target[key] = source[key]; } } return target; }
-
Verwenden Sie
Object.create(null)
für reine Datenobjekte: Wenn Sie Objekte erstellen, die hauptsächlich Daten speichern und nicht vonObject.prototype
erben müssen, erstellen Sie sie mitObject.create(null)
. Dies erstellt ein Objekt ohne Prototyp und macht es immun gegenObject.prototype
-Vergiftung.const dataContainer = Object.create(null);
-
Object.freeze(Object.prototype)
(Nicht empfohlen für die Produktion): Obwohl theoretisch möglich, sollte das Einfrieren vonObject.prototype
mitObject.freeze(Object.prototype)
mit äußerster Vorsicht angegangen werden. Viele Bibliotheken und Frameworks verlassen sich möglicherweise auf die Änderung oder Erweiterung vonObject.prototype
, und das Einfrieren kann zu unerwartetem Verhalten oder Fehlern führen. -
Verwenden Sie Bibliotheken mit integrierten Schutzmechanismen: Nutzen Sie Bibliotheken, die mit Blick auf Sicherheit entwickelt wurden und bekannte Schutzmaßnahmen gegen Prototype Pollution aufweisen (z. B. wurde
lodash.merge
gepatcht, aber überprüfen Sie immer die Version).
Request Smuggling: Mechanismen und Auswirkungen
HTTP Request Smuggling tritt auf, wenn ein Angreifer Mehrdeutigkeiten ausnutzt, wie die Länge einer HTTP-Nachricht bestimmt wird. HTTP/1.1 bietet zwei primäre Header zur Angabe der Nachrichtenkörperlänge: Content-Length
und Transfer-Encoding
. Wenn ein Front-End-Server (wie ein Reverse-Proxy oder Load Balancer) und ein Back-End-Server diese Header unterschiedlich interpretieren, kann ein Angreifer eine "geschmuggelte" zweite, unzulässige Anfrage im Körper der ersten "schmuggeln".
Die gängigen Angriffsvektoren beinhalten
- CL.TE (Content-Length am Front-End, Transfer-Encoding am Back-End): Front-End verwendet
Content-Length
, Back-End verwendetTransfer-Encoding
. - TE.CL (Transfer-Encoding am Front-End, Content-Length am Back-End): Front-End verwendet
Transfer-Encoding
, Back-End verwendetContent-Length
. - TE.TE (Transfer-Encoding an beiden, aber unterschiedliche Interpretationen): Beide verwenden
Transfer-Encoding
, interpretieren es aber unterschiedlich (z. B. verarbeitet eines eine fehlerhafte Chunks-Kodierung, das andere nicht).
Lassen Sie uns einen konzeptionellen CL.TE
-Angriff veranschaulichen:
POST /search HTTP/1.1 Host: vulnerable.com Content-Length: 13 Transfer-Encoding: chunked 0 <-- Chunk-Größe von 0 signalisiert das Ende des Chunks-Körpers <-- Leere Zeile beendet den ersten Chunked-Teil GET /admin HTTP/1.1 Host: vulnerable.com Foo: bar
Front-End (interpretiert Content-Length: 13
): Sieht den gesamten Anfragekörper als 0\r\n\r\nGET /admin...
. Es leitet diesen gesamten Block an das Back-End weiter.
Back-End (interpretiert Transfer-Encoding: chunked
): Verarbeitet den 0\r\n\r\n
-Teil als Ende des Chunks-Körpers der ersten Anfrage. Die nachfolgende GET /admin HTTP/1.1...
-Anfrage wird dann als separate, neue Anfrage von derselben Verbindung vom Front-End-Server behandelt.
Die Auswirkungen können schwerwiegend sein:
- Umgehung von Web Application Firewalls (WAFs): Bösartige Anfragen können innerhalb legitimer Anfragen versteckt werden.
- Zugriff auf interne Endpunkte: Geschmuggelte Anfragen können so aussehen, als kämen sie vom vertrauenswürdigen Reverse-Proxy, was den Zugriff auf interne APIs oder Admin-Oberflächen ermöglicht.
- Cache Poisoning: Ein Angreifer kann eine Anfrage schmuggeln, die dazu führt, dass der Proxy eine bösartige Antwort für eine legitime URL cacht, was nachfolgende Benutzer beeinträchtigt.
- Session Hijacking: Manipulation von Cookies oder Sitzungstokens innerhalb der geschmuggelten Anfrage.
Abwehrmaßnahmen gegen Request Smuggling
Das Node.js http
-Modul ist im Allgemeinen robust gegenüber Request-Smuggling-Problemen innerhalb des Node.js-Servers selbst, da es die HTTP/1.1-Parsing-Regeln strikt befolgt. Die primäre Schwachstelle liegt in der Interaktion zwischen Node.js-Anwendungen und vorgelagerten Proxys/Load Balancern.
-
Konsistentes HTTP-Parsing sicherstellen: Die wichtigste Abwehr ist, sicherzustellen, dass alle Komponenten Ihres Anwendungsstacks (Load Balancer, Proxys und Ihr Node.js-Server) einen konsistenten und strikten HTTP/1.1-Parser verwenden.
- Konfiguration: Konfigurieren Sie Ihre Front-End-Proxys (Nginx, HAProxy, AWS ELB/ALB usw.) so, dass sie die HTTP/1.1-Spezifikationen strikt durchsetzen. Insbesondere sollten sie Anfragen ablehnen, die sowohl
Content-Length
- als auchTransfer-Encoding
-Header enthalten, oder sie eindeutig behandeln. - Einheitlicher Standard: Idealerweise sollten alle Komponenten entweder ausschließlich
Content-Length
verwenden oderTransfer-Encoding: chunked
konsistent verarbeiten. - Verbot beider: Konfigurieren Sie Proxys so, dass Anfragen, die sowohl
Content-Length
- als auchTransfer-Encoding
-Header enthalten, verweigert werden, da dies ein gängiger Angriffsvektor ist.
- Konfiguration: Konfigurieren Sie Ihre Front-End-Proxys (Nginx, HAProxy, AWS ELB/ALB usw.) so, dass sie die HTTP/1.1-Spezifikationen strikt durchsetzen. Insbesondere sollten sie Anfragen ablehnen, die sowohl
-
Upgrade auf HTTP/2: HTTP/2 (und HTTP/3) verwendet eine Framework-basierte Nachrichtenstruktur, die Request Smuggling aufgrund des Fehlens von Mehrdeutigkeiten bei der Bestimmung der Nachrichtenkörperlänge schwierig, wenn nicht unmöglich macht. Wenn möglich, konfigurieren Sie Ihre Infrastruktur so, dass HTTP/2 End-to-End verwendet wird. Wenn nur die Client-zu-Proxy-Verbindung HTTP/2 ist und Proxy-zu-Backend HTTP/1.1, kann das Risiko weiterhin bestehen.
-
Verbindungen bei Mehrdeutigkeit schließen: Ein robuster Abwehrmechanismus für Proxys ist das sofortige Schließen der Client-Verbindung, wenn eine Mehrdeutigkeit bei HTTP-Headern erkannt wird. Dies verhindert, dass ein Angreifer mehrere Anfragen über dieselbe Verbindung sendet.
-
Regelmäßige Sicherheitsscans und Tests: Setzen Sie spezialisierte Sicherheitsscanner ein und führen Sie Penetrationstests durch, um potenzielle Request-Smuggling-Schwachstellen in Ihrer Infrastruktur zu identifizieren. Tools wie die "HTTP Request Smuggler"-Erweiterung von Burp Suite können hierbei sehr effektiv sein.
-
Einheitlichen Proxy oder Managed Service nutzen: Die Verlass auf gut gewartete und praxiserprobte Reverse-Proxys oder verwaltete Load-Balancing-Dienste (wie AWS ALB oder Google Cloud Load Balancer) kann das Risiko erheblich reduzieren, da diese Dienste oft mit robuster HTTP-Parsing- und Sicherheitsfunktionen entwickelt wurden. Stellen Sie sicher, dass sie immer auf die neuesten sicheren Versionen aktualisiert werden.
Fazit
Die Absicherung von Node.js-Webanwendungen gegen hochentwickelte Bedrohungen wie Prototype Pollution und Request Smuggling erfordert ein tiefes Verständnis der zugrunde liegenden JavaScript-Mechanismen und HTTP-Protokolle sowie eine sorgfältige Implementierung von Präventivmaßnahmen. Durch sorgfältige Validierung von Benutzereingaben, Entwicklung robuster Objektzusammenführungsstrategien und Sicherstellung eines konsistenten und strikten HTTP-Parsings über den gesamten Anwendungsstack hinweg können Entwickler ihre Systeme erheblich stärken. Proaktive Sicherheitspraktiken sind von größter Bedeutung, um widerstandsfähige und vertrauenswürdige Webdienste aufzubauen.