Was ist __proto__-Injection
Fazit: JSON.parse behandelt __proto__ als gewöhnlichen Schlüssel; der Schaden entsteht in einem rekursiven Merge, der in Object.prototype schreibt, sodass jedes Objekt Angreifereigenschaften erbt.

Kurz gesagt: Blockieren Sie __proto__, constructor und prototype vor jedem Merge, führen Sie nicht vertrauenswürdige Eingaben in Object.create(null) zusammen und ergänzen Sie eine Prototyp-Zusicherung in CI; Bibliotheken zu patchen allein genügt nicht.
In einem Satz: Ein nicht vertrauenswürdiger Schlüssel namens __proto__ trifft auf einen rekursiven Merge, und der Merge schreibt in den Prototyp.
Eine Reproduktion
function merge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = target[key] || {};
merge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
merge({}, JSON.parse('{"__proto__": {"isAdmin": true}}'));
console.log({}.isAdmin); // true
Kein Geschäftsobjekt wurde berührt. Object.prototype wurde es, und jedes Objekt trägt jetzt isAdmin.
Warum JSON.parse nicht schuld ist
const data = JSON.parse('{"__proto__": {"a": 1}}');
Object.getOwnPropertyNames(data); // [ '__proto__' ]
Object.getPrototypeOf(data) === Object.prototype; // true, untouched
__proto__ kommt als einfache eigene Eigenschaft an. Die Gefahr liegt vollständig im Code, der später zusammenführt, tief kopiert oder nach Pfad setzt – _.merge, manche Object.assign-Muster und unzählige handgeschriebene set(obj, path, value)-Helfer.
Auswirkung
- Autorisierungsumgehung:
if (user.isAdmin)wird bedingungslos wahr - Datenverfälschung: unerwartete Felder serialisieren in gespeicherte Datensätze
- Denial of Service: große Objekte am Prototyp vergrößern den Speicher dauerhaft
- Eskalation: in Kombination mit Template-Engines oder Command-Runnern hat das zu Remote Code Execution geführt
Vier Verteidigungen
Die Schlüssel filtern.
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
Nur __proto__ zu blockieren genügt nicht: constructor.prototype erreicht dieselbe Stelle.
In ein Objekt ohne Prototyp zusammenführen.
const safeTarget = Object.create(null);
Ohne Prototypenkette ist __proto__ nur ein String-Schlüssel.
Object.hasOwn statt in verwenden. in konsultiert die Prototypenkette.
Abhängigkeiten gepatcht halten. lodash, deepmerge und json-patch haben alle Fixes für diese Fehlerklasse ausgeliefert. Beobachten Sie npm audit.
Prüfen, dass es behoben bleibt
Geben Sie eine Payload mit __proto__ und constructor durch Ihren Merge und prüfen Sie dann:
console.log({}.polluted); // undefined
console.log(Object.prototype.polluted); // undefined
Eine Zusicherung in CI, nahezu null Kosten, fängt Regressionen aus späteren Refactorings ab.
FAQ
Welche Schlüssel sollte ein Merge herausfiltern?
Blockieren Sie __proto__, constructor und prototype gemeinsam. Nur __proto__ zu filtern genügt nicht, weil constructor.prototype denselben Prototyp erreicht.
Warum stoppt Object.create(null) den Schreibvorgang?
Ein Objekt ohne Prototyp hat keine Prototypenkette, __proto__ bleibt also ein gewöhnlicher String-Schlüssel. Es funktioniert als Datencontainer, ersetzt aber nicht die Schlüsselfilterung oder Abhängigkeitsupdates.
Wie teste ich Prototype Pollution in CI?
Senden Sie eine Payload mit __proto__ und constructor durch Ihren Merge und prüfen Sie dann, dass {}.polluted und Object.prototype.polluted undefined sind. Eine Zusicherung kostet fast nichts und fängt Regressionen aus späteren Refactorings ab.