Rückblick: Prototypen
Im ersten Teil haben wir gelernt:
- Jedes JavaScript-Objekt hat einen Prototypen.
- Wenn eine Eigenschaft nicht im Objekt selbst steht, sucht JS in der Prototype Chain weiter.
- Änderungen am Prototypen wirken global.
Damit haben wir das Fundament gelegt. Nun schauen wir uns an, wie Angreifer genau das ausnutzen.
Die kurze Definition
Prototype Pollution ist eine Sicherheitslücke, bei der Angreifer es schaffen, den Prototypen von Objekten zu verändern, indem sie unkontrollierte Eingaben einschleusen.
Ergebnis: Plötzlich tauchen neue Eigenschaften in allen Objekten auf – auch da, wo sie nicht hingehören.
Warum das problematisch ist
Stell dir vor, ein Entwickler schreibt:
function isAdmin(user) {
return !!user.isAdmin;
}
Er geht davon aus, dass user.isAdmin
nur dann wahr ist, wenn er es explizit gesetzt hat.
Aber:
Wenn jemand den Prototyp manipuliert und dort isAdmin = true
einfügt, gilt das plötzlich für jedes Objekt.
Object.prototype.isAdmin = true;
console.log(isAdmin({ name: "Alice" })); // true
Das heißt: Mit einem einzigen Trick werden sämtliche Prüfungen ausgehebelt.
Der Einstiegspunkt: Benutzer-Eingaben
Prototype Pollution passiert nicht einfach von alleine.
Sie entsteht, wenn eine Anwendung unseriöse Eingaben vom Nutzer nimmt und diese ohne Kontrolle in Objekte schreibt.
Typische Szenarien:
- Querystrings:
?isAdmin=true
- JSON-Bodies:
{"isAdmin": true}
- Formulareingaben
- Konfigurationsdateien
Die App baut daraus Objekte – und oft gibt es Funktionen, die Eingaben in bestehende Objekte hinein-mergen.
Beispiel: Ein unsicherer Deep-Merge
Viele Libraries haben eine Funktion wie merge(target, source)
.
function unsafeMerge(target, source) {
for (let key in source) {
if (typeof source[key] === "object") {
target[key] = unsafeMerge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
Die Funktion durchläuft alle Keys und kopiert sie ins Zielobjekt. Klingt praktisch.
Die magischen Schlüssel
Jetzt kommt der Trick: Angreifer wissen, dass bestimmte Schlüssel in Objekten besonders sind.
__proto__
In vielen JS-Engines ist __proto__
ein versteckter Link auf den Prototypen.
constructor.prototype
Jedes Objekt hat einen constructor
. Dessen prototype
ist wiederum der Prototyp.
prototype
Wenn Code mit Klassen oder Funktionen arbeitet, ist prototype
oft der Einstiegspunkt.
Angriff mit „constructor.prototype“
Nehmen wir unser unsicheres merge
.
Angreifer schicken:
{
"constructor": {
"prototype": {
"isAdmin": true
}
}
}
Was passiert?
merge
geht durch alle Keys.- Es landet bei
constructor
. - Darunter kommt
prototype
. - Am Ende wird
Object.prototype.isAdmin = true
gesetzt.
Und schon:
console.log(({}).isAdmin); // true
Alle Objekte haben jetzt isAdmin
.
Angriff mit __proto__
Manchmal reicht sogar das hier:
{
"__proto__": {
"polluted": "YES"
}
}
Je nach Parser wird daraus:
Object.prototype.polluted = "YES";
Das Ergebnis:
console.log(({}).polluted); // "YES"
Wie kommen solche Objekte zustande?
Sehr oft durch Parser oder Body-Handler, die verschachtelte Strukturen erlauben.
Beispiel: Querystring
?__proto__[polluted]=YES
Ein Querystring-Parser könnte daraus ein Objekt bauen:
{
"__proto__": { "polluted": "YES" }
}
Wenn dieses Objekt dann in die App gemerged wird, ist der Schaden da.
Beispiel: JSON-Body
Ein Angreifer schickt in einem POST-Request:
{
"constructor": {
"prototype": {
"isAdmin": true
}
}
}
Die App denkt, es sei einfach ein verschachteltes Objekt – und verarbeitet es brav.
Warum ist das so gefährlich?
Prototype Pollution ist tückisch, weil sie:
- Global wirkt – alle Objekte sind betroffen.
- Schwer zu entdecken ist – Tests prüfen meist nur normale Inputs.
- Viele Folgen haben kann:
- Sicherheitsprüfungen werden umgangen.
- Debug-Flags werden eingeschaltet.
- Eingebaute Methoden wie
toString
können überschrieben werden → Crash oder Denial of Service.
Beispiel: Feature-Toggles
Eine App nutzt Feature-Flags:
if (user.betaEnabled) {
showBetaFeature();
}
Ein Angreifer polluted den Prototypen:
Object.prototype.betaEnabled = true;
Jetzt ist für alle Nutzer das Beta-Feature aktiv – selbst wenn es nie freigeschaltet wurde.
Beispiel: Denial of Service
Angreifer überschreiben eine Kernfunktion:
Object.prototype.toString = null;
Viele Libraries rufen intern toString()
auf.
Wenn das plötzlich null
ist, werfen unzählige Stellen Fehler – die App stürzt ab.
Analogie: Wasserleitungen in der Stadt
Stell dir vor, jedes Haus in einer Stadt hat seine eigene Wasserleitung (eigene Eigenschaften).
Alle Leitungen hängen aber am gleichen Hauptrohr (Prototyp).
Wenn jemand Gift ins Hauptrohr kippt, sind alle Häuser betroffen.
So ähnlich ist es bei Prototype Pollution: Eine einzige Manipulation vergiftet das ganze System.
Was du bis hier mitnehmen solltest
- Prototype Pollution = Manipulation von Prototypen über unkontrollierte Eingaben.
- Angreifer nutzen Schlüssel wie
__proto__
oderconstructor.prototype
. - Typische Einstiegspunkte sind Parser und Merge-Funktionen.
- Folgen: Sicherheitsprüfungen brechen, Features werden ungewollt aktiviert, ganze Apps crashen.
Schreibe einen Kommentar