Teil 2 – Was ist Prototype Pollution?

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:

  1. Global wirkt – alle Objekte sind betroffen.
  2. Schwer zu entdecken ist – Tests prüfen meist nur normale Inputs.
  3. 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__ oder constructor.prototype.
  • Typische Einstiegspunkte sind Parser und Merge-Funktionen.
  • Folgen: Sicherheitsprüfungen brechen, Features werden ungewollt aktiviert, ganze Apps crashen.

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert