FlawOpen

flawopen.com/Schwachstellen/JavaScript Prototype Pollution

JavaScript Prototype Pollution

Hoher Schweregrad CWE-1321 Lieferkette und Laufzeit
Einfach erklärt — Ein gemeinsames Regelbuch

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.

Ziel:JavaScript auf Server oder Browser, das nicht vertrauenswürdige Schlüssel zusammenführt oder vom Nutzer gesteuerte Eigenschaftspfade schreibt
Angriffsweg:Nicht vertrauenswürdige Schlüssel wie __proto__ oder ein Pfad constructor.prototype, der zu einem unsicheren Schreibzugriff führt
Auswirkung:Abhängig von einem erreichbaren Gadget: Logikänderung, Denial of Service, XSS oder in manchen Serverfällen Codeausführung; nichts davon ist automatisch
Abhilfe:Mit Allowlist oder Schema validieren; für nicht vertrauenswürdige Schlüssel Map oder prototypfreie Dictionaries nutzen

Grundbegriffe 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

Schritt 1

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.

Schritt 2

Unsicherer dynamischer Schreibzugriff

Ein rekursives Merge oder Pfad-Setter folgt diesen Schlüsseln und schreibt dynamisch, ohne jedes Pfadsegment zu prüfen.

Schritt 3

Ä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.

Schritt 4

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.

utils.js (Verwundbares rekursives Merge)Verwundbar
// 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);
utils.js (Profilparser mit Allowlist)Abgesichert
// 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

Abwehrmaßnahmen und Präventionsregeln

Häufige Fragen

Vergiftet JSON.parse() den Prototyp selbst?

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?

Behebt Object.freeze(Object.prototype) das Problem?

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.

Ist TypeScript geschützt?

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.

Weitere Sicherheitsthemen und Analysen:Vollständiges Sicherheitsverzeichnis →