flawopen.com/Schwachstellen/JavaScript Prototype Pollution
JavaScript Prototype Pollution
Stellen Sie sich eine Werkstatt vor, in der jedes neue Gerät ein gemeinsames Regelbuch fragt, wenn seine eigenen Einstellungen keine Antwort geben. Schreibt ein unsicherer Mitarbeiter eine vom Angreifer kontrollierte Regel hinein, können andere Geräte sie als Standard verwenden. Bei Prototype Pollution können unsichere dynamische Schreibzugriffe den Prototyp eines Objekts verändern, manchmal Object.prototype. Das wirkt nicht automatisch global; die Auswirkung setzt voraus, dass Anwendungscode der geerbten Eigenschaft später vertraut.
__proto__ oder ein Pfad constructor.prototype, der zu einem unsicheren Schreibzugriff führtMap oder prototypfreie Dictionaries nutzenGrundbegriffe und Subsysteme
- Object.prototype
- Gemeinsamer Prototyp, den gewöhnliche Objekte erben. Seine Manipulation kann viele Objekte betreffen, aber nicht jedes JavaScript-Objekt und nicht jeden Realm.
- __proto__
- Veralteter Zugriff auf gewöhnlichen Objekten, der ihren Prototyp liest oder setzt. In JSON-Text ist dieselbe Schreibweise zunächst ein normaler Eigenschaftsname.
- Rekursives Deep Merge
- Kopierfunktion, die verschachtelte Schlüssel durchläuft und in ein anderes Objekt schreibt. Gefährlich, wenn nicht vertrauenswürdige Schlüssel prototypbezogene Eigenschaften oder unsichere Pfade auswählen.
- CWE-1321
- Unzureichend kontrollierte Änderung von Objekt-Prototypattributen. Die Schwachstelle ist die unkontrollierte Mutation; die spätere Auswirkung hängt vom erreichbaren Anwendungsverhalten ab.
Angriffsablauf Schritt für Schritt
Nicht vertrauenswürdige Eigenschaften
Ein Endpunkt akzeptiert vom Angreifer kontrollierte Objektschlüssel. Das Parsen von JSON mit einer eigenen __proto__-Eigenschaft ändert für sich genommen keinen Prototyp.
Unsicherer dynamischer Schreibzugriff
Ein rekursives Merge oder Pfad-Setter folgt diesen Schlüsseln und schreibt dynamisch, ohne jedes Pfadsegment zu prüfen.
Änderung des Ziel- oder gemeinsamen Prototyps
Je nach Ziel und Schreibreihenfolge ändert der Vorgang den Prototyp des Ziels oder erreicht einen gemeinsamen Prototyp wie Object.prototype. Das sind unterschiedliche Ergebnisse.
Ein Gadget bestimmt die Auswirkung
Ein späteres Gadget liest den geerbten Wert bei einer sicherheitsrelevanten Operation. Die Folgen hängen davon ab; Authentifizierungsumgehung oder Codeausführung dürfen nicht vorausgesetzt werden.
Mechanismus und Ursache
Die JavaScript-Eigenschaftssuche folgt der Prototypkette. Ein verwundbares Merge oder ein Pfad-Setter kann nicht vertrauenswürdige Schlüssel wie __proto__ oder constructor.prototype zu einem Prototyp-Schreibzugriff führen lassen. Ein eigenes __proto__-Feld aus JSON.parse ist noch keine Pollution; weiterer Code muss es durch eine unsichere Zuweisung interpretieren. Eine Änderung am Zielprototyp betrifft dieses Ziel; eine Änderung an Object.prototype betrifft gewöhnliche Objekte, die ihn erben. Erst ein separates Gadget, das die geerbte Eigenschaft nutzt, erzeugt eine Sicherheitsauswirkung.
// VERWUNDBAR: angreifergesteuerte Schlüssel erreichen rekursive Schreibzugriffe
function merge(target, source) {
for (const key in source) {
const value = source[key];
if (value && typeof value === 'object') {
if (!target[key]) target[key] = {};
merge(target[key], value);
} else {
target[key] = value;
}
}
return target;
}
// JSON.parse erzeugt eine eigene __proto__-Dateneigenschaft; das unsichere Merge ist das Risiko.
const input = JSON.parse('{"__proto__": {"isAdmin": true}}');
merge({}, input);
// ABGESICHERT: nur die vom Endpunkt vorgesehenen Felder kopieren
const PROFILE_KEYS = new Set(['displayName', 'theme']);
function parseProfile(input) {
if (!input || typeof input !== 'object' || Array.isArray(input)) {
throw new TypeError('profile must be an object');
}
const profile = Object.create(null);
for (const key of Object.keys(input)) {
if (!PROFILE_KEYS.has(key)) continue;
const value = input[key];
if (typeof value !== 'string' || value.length > 80) {
throw new TypeError('invalid profile field');
}
profile[key] = value;
}
return profile;
}
Angriffs- und Ausnutzungsablauf
- 1Eingabe: Eine Anfrage enthält einen unerwarteten Objektschlüssel wie
{"__proto__": {"admin": true}}. JSON-Parsing allein verändert keinen Prototyp. - 2Unsicherer Schreibzugriff: Ein generisches Merge oder ein Pfad-Setter verarbeitet den Schlüssel ohne Allowlist.
- 3Prototypzugriff: Je nach Codepfad kann der Schreibzugriff den Zielprototyp ändern oder einen gemeinsamen Prototyp erreichen.
- 4Auswirkungs-Gadget: Die Pollution beeinflusst eine sensible Entscheidung nur, wenn späterer Code einer geerbten Eigenschaft vertraut. Prüfen Sie das tatsächliche Gadget und den Autorisierungspfad.
Abwehrmaßnahmen und Präventionsregeln
- ✓Schema-Allowlists bevorzugen: Erzeugen Sie das Ziel nur aus Feldern, die der Endpunkt akzeptieren soll. Wenn ein generischer Pfadschreiber nötig ist, weisen Sie prototypbezogene Segmente auf jeder Ebene zurück.
- ✓Sichere Dictionaries verwenden: Speichern Sie beliebige Nutzerschlüssel in
Mapoder einemObject.create(null)-Dictionary; prüfen Sie Daten vor der Übergabe an APIs, die gewöhnliche Objekte erwarten. - ✓Eigene Eigenschaften prüfen: Verwenden Sie
Object.hasOwn(record, key)für sicherheitsrelevante Prüfungen; geerbte Standardwerte dürfen keine Aktionen autorisieren. - ✓Runtime-Härtung als Defense in Depth einsetzen: Node.js
--disable-proto=throwentfernt einen Angriffsweg, blockiert aber nichtconstructor.prototype. Eingebaute Objekte einzufrieren kann Polyfills und Abhängigkeiten brechen; prüfen Sie die Kompatibilität und ersetzen Sie damit niemals sichere Schreibzugriffe.
Häufige Fragen
Nein. JSON.parse() erstellt eine eigene Dateneigenschaft namens __proto__ und ruft den geerbten Setter nicht auf. Das Risiko entsteht, wenn ein späteres Merge oder eine Zuweisung den Setter auslöst. Siehe Verursacht JSON.parse() Prototype Pollution?
Es kann Schreibvorgänge auf das eingefrorene Object.prototype selbst verhindern, stoppt aber keinen Setter, der den Prototyp eines einzelnen Objekts ändert, und schützt nicht jeden anderen Prototyp. Es kann außerdem Bibliotheken beschädigen, die eingebaute Objekte erweitern. Nur als zusätzliche Schutzmaßnahme, nicht als Datenkorrektur verwenden.
Nein. Typen werden zur Laufzeit entfernt. Ein als Record<string, string> deklarierter Wert kann weiterhin einen __proto__-Schlüssel enthalten; validiere nicht vertrauenswürdige Schlüssel und erstelle Objekte sicher.
- MDN — JavaScript Prototype Pollution
- OWASP — Leitfaden gegen Prototype Pollution
- CWE-1321 — Unzureichend kontrollierte Änderung von Objekt-Prototypattributen
- MDN — Object.prototype.__proto__-Accessor