Prototype Pollution ist eine eigentümlich JavaScript-spezifische Schwachstelle, die in den letzten Jahren zur eigenständigen Klasse geworden ist. Anders als XSS oder SQLi zielt sie nicht auf einzelne Eingaben, sondern auf das Prototyp-Modell von JavaScript selbst: wenn ein:e Angreifer:in Properties auf Object.prototype setzt, erbt jedes Objekt im Programm diese Properties — und Anwendungs-Logik, die mit Default-Werten rechnet, läuft anders als gedacht. Dieser Artikel erklärt das Konzept, die typischen Sinks, die historische CVE-Welle und die Schutz-Patterns.
Was Prototype Pollution ist
In JavaScript hat jedes Objekt eine Prototyp-Kette. Wenn du auf ein Property zugreifst, das ein Objekt selbst nicht hat, geht JavaScript die Prototyp-Kette hoch — bis zum Object.prototype als Wurzel. Wenn dort eine Property liegt, wird sie als „virtuelles Property" des Objekts wahrgenommen.
Konkret:
const obj = {};
console.log(obj.foo); // undefined
// Wenn jemand Object.prototype manipuliert ...
Object.prototype.foo = 'gepwnt';
console.log(obj.foo); // 'gepwnt' — ohne dass obj selbst je foo hatte
console.log({}.foo); // 'gepwnt' — gilt für ALLE ObjekteWer Object.prototype modifiziert, verändert jedes Objekt im Programm. Wenn ein:e Angreifer:in Eingaben so platziert, dass sie zu einem Object.prototype-Property werden, ist die Wirkung global.
Wie der Angreifer dahinkommt:
JavaScript hat zwei Wege, an die Prototyp-Kette zu kommen:
__proto__— älterer Property-Zugriff. In JSON-Eingaben oft durchschleifbar.constructor.prototype— über den Constructor.__proto__überObject.assign/ Spread — manche Implementierungen ignorieren__proto__korrekt, andere nicht.
Typische Angriffs-Eingaben:
{ "__proto__": { "isAdmin": true } }
{ "constructor": { "prototype": { "isAdmin": true } } }Wenn diese Eingabe in eine rekursive Merge-Operation läuft, die Properties tief kopiert, landet isAdmin: true als globales Property auf Object.prototype. Anschließend hat jedes Objekt im Programm obj.isAdmin === true.
Klassische Sinks
Wo entstehen Prototype-Pollution-Lücken? Drei typische Code-Patterns:
1. Tiefe Merge-Operationen.
// Schadhaft — rekursives Merge ohne __proto__-Schutz
function deepMerge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object') {
target[key] = target[key] || {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
const config = {};
deepMerge(config, JSON.parse(userInput));
// Wenn userInput { "__proto__": { "isAdmin": true } } enthält,
// setzt deepMerge Object.prototype.isAdmin = true2. Property-Setter über String-Pfade.
// Schadhaft — Set-by-path ohne __proto__-Schutz
function setByPath(obj, path, value) {
const keys = path.split('.');
let target = obj;
for (let i = 0; i < keys.length - 1; i++) {
if (!target[keys[i]]) target[keys[i]] = {};
target = target[keys[i]];
}
target[keys.at(-1)] = value;
}
setByPath(config, '__proto__.isAdmin', true);
// setzt Object.prototype.isAdmin = true3. Query-String / Form-Parser.
// Manche ältere qs-Versionen waren anfällig:
// ?__proto__[isAdmin]=true wurde zu { __proto__: { isAdmin: true } }
// ... und mit Object.assign in den Heap geschleust4. Object-Cloning per JSON-Loop.
Falsch implementierte deep-clone-Funktionen können __proto__ durchschleifen.
5. ORM- und Form-Library-Defaults. Manche Libraries setzen unsichere Default-Properties über Prototyp-Patterns, was kompromittierbar wird.
Was die Schwachstelle ausnutzt
Prototype Pollution allein ist meist nicht der Endpunkt — sie ist ein Eskalations-Vektor. Die Frage ist: was macht die Anwendung mit den manipulierten Default-Werten?
Typische Eskalationen:
- Authentifizierungs-Bypass. Wenn ein Auth-Check liest
if (user.isAdmin), undisAdminist nun durch Pollutiontrueauf jedem Objekt — Bypass. - CSP-Bypass / XSS. Wenn die App eine Library nutzt, die ein Property aus
Object.prototypeliest (z. B. ein DOM-Library, ein Template-Engine), kann ein manipuliertes Property zu XSS führen. - RCE in Node.js. In bestimmten Konstellationen — z. B. wenn
child_process.execmit einemoptions-Objekt aufgerufen wird, das von Prototype Pollution betroffen ist — kann ein manipuliertes Default-Property zu RCE führen. Forschung dazu von Snyk und PortSwigger seit ca. 2018. - DoS. Manipulation von Default-Werten in Critical-Path-Code kann Anwendung zum Absturz bringen.
Reale Beispiele:
- Lodash (CVE-2018-3721, CVE-2019-10744, CVE-2020-8203) — mehrere Wellen, jeweils Patches und neue Funde.
_.merge,_.set,_.defaultsDeepwaren betroffen. - Hoek (Hapi, CVE-2018-3728) — Pollution-Vektor in
merge-Funktion. - minimist (CVE-2020-7598) — Command-Line-Parser-Library, häufig in Build-Tools.
- jQuery 3.x (CVE-2019-11358) — Prototype Pollution in
$.extend(true, ...). - qs (Query-String-Parser) — mehrere Versionen anfällig.
- express-fileupload, mongoose, async, mqtt, set-value, dot-prop — Liste ließe sich verlängern.
Die Liste betroffener NPM-Pakete ist groß. Wer eine moderne Node.js-Anwendung baut, hat sehr wahrscheinlich indirekt Code im Dependency-Baum, der historisch Pollution-Bugs hatte.
Pollution verhindern
Mehrere komplementäre Schutz-Patterns:
1. Statt {} lieber Object.create(null) verwenden.
Objekte ohne Prototyp existieren — und sind immun gegen Pollution.
// Pollution-resistent
const config = Object.create(null);
// config hat keine __proto__-Property; Setzen läuft als normales Property
config.__proto__ = { isAdmin: true }; // setzt nur lokal, kein Effekt auf andere
console.log({}.isAdmin); // undefined — globale Object.prototype unverändert2. Object.freeze(Object.prototype).
Friert die Prototyp-Kette ein, sodass keine neuen Properties hinzufügbar sind.
// Beim Programm-Start
Object.freeze(Object.prototype);
Object.freeze(Array.prototype);
Object.freeze(Function.prototype);
// Versuche, Properties zu setzen, scheitern silent oder werfen Error (strict mode)Vorsicht: Manche Libraries verlassen sich auf Erweiterung von Prototypen — der Freeze kann Existing-Code brechen. Im Default-Setup eines neuen Projekts sicher; bei Migration zu testen.
3. Schema-Validierung mit Allowlist.
Statt rohe Objekte aus JSON-Eingaben durchzuschleifen, mit Schema-Library (Zod, Joi, Yup, Ajv) validieren. Erlaubte Properties explizit, andere verworfen. Verhindert sowohl Pollution als auch viele andere Klassen.
import { z } from 'zod';
const ConfigSchema = z.object({
name: z.string(),
theme: z.enum(['light', 'dark']),
maxItems: z.number().int().positive(),
}).strict(); // unbekannte Properties (inkl. __proto__) werden abgelehnt
const parsed = ConfigSchema.parse(JSON.parse(userInput));
// parsed ist garantiert frei von __proto__-Tricks4. Sichere Merge-/Set-Libraries nutzen.
Aktualisierte Versionen von Lodash, Hoek u. a. haben Schutz eingebaut. Aber: nicht alle Funktionen sind safe — die Doku der Library lesen.
5. __proto__ und constructor als Schlüssel ablehnen.
Eigene Merge-/Set-Implementierungen können explizit gefährliche Keys filtern:
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
function safeMerge(target, source) {
for (const key in source) {
if (FORBIDDEN_KEYS.has(key)) continue;
if (!Object.prototype.hasOwnProperty.call(source, key)) continue;
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = target[key] || {};
safeMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}6. Map statt Plain-Objects für User-Input.
JavaScript-Maps haben kein Prototyp-Konzept im selben Sinn — sie sind strukturell anders. Wenn du User-Input als Lookup-Struktur brauchst, kann Map sicherer sein als {}.
Browser-Seite: Client-Side Prototype Pollution
Prototype Pollution gibt es auch im Browser, nicht nur in Node.js. Im Browser ist die typische Eskalation XSS statt RCE.
Beispiel-Sink im Frontend:
// Schadhaft — Library liest ein Default-Property
function safeRender(element, options) {
const opts = Object.assign({}, options);
if (opts.sanitize !== false) {
element.innerHTML = sanitize(element.innerHTML);
} else {
element.innerHTML = element.dataset.raw; // unsanitized
}
}
// Wenn Object.prototype.sanitize = false gesetzt wurde,
// wird die "innerHTML = raw"-Variante immer genommen — XSS möglichPortSwigger Research hat 2019/2020 systematisch Bibliotheken gescannt und Tausende Pollution-zu-XSS-Vektoren gefunden — viele in Mainstream-Libraries wie jQuery, Vue.js (in alten Versionen), Bootstrap-Komponenten.
Schutz im Frontend:
- Build-Time-Audit der Dependencies (npm audit, Snyk, Dependabot).
- Trusted Types in der CSP — verhindert, dass manipulierte Properties zu DOM-XSS werden. Siehe trusted-types (Kap 11).
Object.freeze(Object.prototype)auch im Browser-Code.
Erkennung in der Praxis
Prototype Pollution ist mit Standard-Werkzeugen schwer automatisch zu finden. Drei nützliche Ansätze:
1. Static Analysis. Tools wie Snyk Code, Semgrep, CodeQL haben Regeln für typische Pollution-Sinks. Sehr effektiv für eigene Anwendung — weniger für Dependencies.
2. Dynamic Testing. Burp Suite Pro mit dem DOM Invader-Plugin testet automatisch Client-Side-Pollution. Für Backend gibt es separate Module.
3. Manuelle Code-Reviews. Spezifisch auf:
merge,mergeDeep,extend,assignmit User-Input.set-Funktionen, die String-Pfade akzeptieren.- Query-String-Parser ohne Schema.
- Cookie-Parser.
4. Dependency-Updates. Die meisten Pollution-CVEs werden in den Hauptbibliotheken gefixt. Wer aktuell hält (Dependabot, Renovate), schließt sie automatisch.
Warum es so eine eigene Klasse ist
Prototype Pollution ist eigentümlich, weil es auf Sprach-spezifischen Eigenheiten beruht. In Python, Java, Go, Rust gibt es das Problem in dieser Form nicht — weil dort entweder kein Prototype-Modell existiert oder die Eingaben nicht so leicht auf interne Klassen-Strukturen mappen.
JavaScript-spezifisch ist daran:
- Das Prototype-Modell als Sprach-Default.
- Die dynamische Property-Assignment (
obj['key'] = valuefür beliebige Strings). - Die JSON-Eingabe, die nahtlos in Objekte konvertiert wird, ohne Type-Information.
Die Klasse hat sich erst ab 2018 als eigenständig herauskristallisiert — vorher wurden Pollution-Bugs als „normale Logik-Fehler" gemeldet. Heute ist sie eine bekannte CVE-Kategorie und in OWASP-Cheat-Sheets dokumentiert.
Besonderheiten
Olivier Arteau hat das Phänomen 2018 populär gemacht
Der NorthSec-Vortrag „Prototype pollution attacks in NodeJS applications" (2018) von Olivier Arteau hat das Konzept ins Sicherheits-Mainstream gebracht. Vorher gab es einzelne CVEs, aber keine zusammenhängende Klasse.
PortSwigger Research zu Client-Side Pollution
Gareth Heyes hat 2020 systematisch Browser-Libraries auf Pollution-zu-XSS-Vektoren gescannt. Veröffentlichung 2022 dokumentiert systematische Test-Methodik. Das hat Client-Side-Pollution erstmals als eigenständiges Thema sichtbar gemacht.
Lodash hat viele Wellen mitgemacht
Lodash ist eine der meistgenutzten JS-Libraries (Hunderte Millionen Downloads pro Woche). Mehrere CVE-Wellen über die Jahre — jeder Patch behebt eine Methode, dann wird die nächste gefunden. Heute (Lodash 4.17.21+) sind die bekannten Vektoren gepatcht.
Object.freeze(Object.prototype) als "Strict Web Apps"
Manche Hochsicherheits-Stacks frieren Prototypes beim Programm-Start ein. Funktioniert in modernen Stacks (Next.js, Vite-Apps) meist ohne Probleme. Vorsicht bei älteren Libraries, die Prototyp-Erweiterungen erwarten.
Node.js 20+ hat ein Permission-Modell
Mit Node.js 20 (2023) kam ein experimentelles --permission-Flag, das Datei-System- und Netzwerk-Zugriffe einschränken kann. Reduziert die Eskalations-Möglichkeiten von Pollution-zu-RCE — die Schwachstelle bleibt aber theoretisch ausnutzbar.
TypeScript hilft nicht direkt
TypeScript-Compilation kennt Prototypes nicht als spezielle Klasse. Strict-Mode-Typings (noImplicitAny, strict) helfen indirekt, weil sie unsaubere Code-Patterns aufdecken — aber Pollution selbst ist eine Runtime-Klasse, die TS nicht direkt sieht.
Cross-Stack-Variante: Server-Side-Pollution via Frontend
Ein subtiler Vektor: Frontend baut ein Objekt mit User-Eingabe, schickt es als JSON an Backend. Backend nutzt Pollution-anfällige Library zum Verarbeiten. Frontend ist nicht direkt verwundbar, Backend schon — und der Vektor wird über die normale API-Strecke geliefert. Wichtig bei Architekturen mit mehreren Schichten.
Weiterführende Ressourcen
Externe Quellen
- OWASP — Prototype Pollution
- OWASP Prototype Pollution Prevention Cheat Sheet
- PortSwigger Research — Prototype Pollution Overview
- Snyk — Prototype Pollution Learn
- Olivier Arteau — NorthSec 2018 Materialien
- BlackFan — Client-Side Prototype Pollution List
- DOM Invader (Burp Plugin)
- Node.js Permission Model