Teil 5 – Schutzmaßnahmen und Best Practices gegen Prototype Pollution

Warum Vorbeugung so entscheidend ist

Prototype Pollution ist tückisch:

  • Sie wirkt global.
  • Sie bleibt lange unentdeckt.
  • Sie kann schwerwiegende Folgen haben – von Admin-Bypass bis DoS.

Der beste Schutz ist nicht, Pollution nachträglich „aufzuräumen“, sondern sie gar nicht erst entstehen zu lassen. In diesem Beitrag gehen wir Schritt für Schritt durch die wichtigsten Abwehrstrategien.


1. Schlüssel validieren (Blockliste & Positivliste)

Prototype Pollution passiert fast immer, weil Anwendungen ungeprüfte Eingaben direkt in Objekte übernehmen. Die einfachste Abwehr: gefährliche Schlüssel blockieren.

Blockliste

Mindestens diese Keys sollten niemals aus untrusted Input akzeptiert werden:

  • __proto__
  • prototype
  • constructor

Beispiel:

const FORBIDDEN_KEYS = ["__proto__", "prototype", "constructor"];

function isSafeKey(key) {
  return !FORBIDDEN_KEYS.includes(key);
}

function safeMerge(target, source) {
  for (const key in source) {
    if (!isSafeKey(key)) continue;

    const value = source[key];
    if (value && typeof value === "object" && !Array.isArray(value)) {
      target[key] = safeMerge(target[key] || {}, value);
    } else {
      target[key] = value;
    }
  }
  return target;
}

Positivliste

Noch besser ist es, nur bekannte Keys zuzulassen.
Beispiel:

const ALLOWED_KEYS = ["name", "email", "age"];

function strictMerge(target, source) {
  for (const key of ALLOWED_KEYS) {
    if (key in source) {
      target[key] = source[key];
    }
  }
  return target;
}

2. Sichere Container nutzen

Object.create(null)

Standardobjekte erben von Object.prototype. Aber wenn du Objekte mit Object.create(null) erzeugst, haben sie keinen Prototypen. Damit können sie nicht „vergiftet“ werden.

const safeObj = Object.create(null);

safeObj.foo = "bar";

console.log(safeObj.__proto__); // undefined

Solche „reinen Dictionaries“ sind perfekt, wenn du Benutzereingaben speichern musst.

Map

Wenn du Key-Value-Strukturen brauchst, nimm lieber Map:

const m = new Map();
m.set("__proto__", "harmlos");

console.log(m.get("__proto__")); // "harmlos"

Eine Map hat keine Prototypen-Magie – Keys sind einfach nur Strings.


3. Idempotente & sichere Merge-Funktionen

Viele Schwachstellen entstehen durch naive Merge-Implementierungen. Statt eigene Helfer zu schreiben, nutze:

  • moderne, gepatchte Versionen von Libraries (z. B. Lodash >= 4.17.12)
  • oder schreibe sehr einfache Merges, die nur genau das tun, was du brauchst

Beispiel für eine sichere flache Zuweisung:

Object.assign(target, source);

Das kopiert nur eigene Eigenschaften von source und beachtet keine Prototype-Ketten.


4. Prüfungen robust machen

Own Properties prüfen

Viele Exploits basieren darauf, dass Anwendungen geerbte Eigenschaften akzeptieren. Das lässt sich leicht verhindern:

if (Object.hasOwn(obj, "isAdmin")) {
  // sichere Prüfung
}

Oder kompatibler:

if (Object.prototype.hasOwnProperty.call(obj, "isAdmin")) {
  // nur eigene Eigenschaften
}

Damit werden Prototyp-Manipulationen ignoriert.


5. Parser härten

JSON

Wenn du JSON.parse() nutzt, kannst du einen Reviver verwenden, um gefährliche Schlüssel rauszufiltern:

const FORBIDDEN = ["__proto__", "prototype", "constructor"];

const data = JSON.parse(rawInput, (key, value) => {
  if (FORBIDDEN.includes(key)) return undefined;
  return value;
});

Querystring / YAML

Viele Parser erlauben verschachtelte Strukturen. Prüfe, ob deine Parser-Version Schutz gegen Pollution hat.
Beispiele:

  • qs (Node.js) → ab Version 6.0 fix für __proto__
  • js-yaml → prüft inzwischen auf gefährliche Schlüssel

6. Tiefen- und Größenlimits

Manche Angriffe nutzen extrem tiefe oder breite Objekte, um Parser und Merges zu überlasten.
Setze daher Limits:

function tooDeep(obj, depth = 0, maxDepth = 5) {
  if (depth > maxDepth) return true;
  if (obj && typeof obj === "object") {
    return Object.values(obj).some(v => tooDeep(v, depth + 1, maxDepth));
  }
  return false;
}

Wenn ein Input zu tief ist, → ablehnen.


7. Runtime-Monitoring

Man kann zur Laufzeit prüfen, ob das Object.prototype verdächtige Keys hat:

const suspicious = Object.keys(Object.prototype).filter(
  key => !["constructor", "toString", "valueOf"].includes(key)
);

if (suspicious.length > 0) {
  console.error("Prototype Pollution erkannt!", suspicious);
}

Das ersetzt keine saubere Programmierung, kann aber als Alarmanlage dienen.


8. Security-Tests einbauen

Jede App sollte Unit-Tests gegen Pollution haben. Beispiel:

test("Object.prototype darf nicht manipuliert werden", () => {
  expect(({}).polluted).toBeUndefined();
});

Führe Tests nach Requests aus, die untrusted Input verarbeiten.


9. Kein „falsches Fixen“

Einige vermeintliche Lösungen sind gefährlich:

  • Object.freeze(Object.prototype): Klingt gut, bricht aber viele Libraries, die legitime Prototyp-Erweiterungen nutzen.
  • Nur __proto__ blocken: Angreifer weichen auf constructor.prototype aus.
  • Nur am Ende prüfen: Pollution muss beim Einlesen verhindert werden, nicht erst, wenn es zu spät ist.

10. Organisation & Updates

Prototype Pollution ist auch ein Ökosystem-Problem:

  • Viele Schwachstellen entstehen in beliebten NPM-Paketen.
  • Halte deine Dependencies aktuell.
  • Nutze Tools wie npm audit oder snyk, um bekannte CVEs früh zu erkennen.

Analogie: Impfungen für das System

Prototype Pollution ist wie eine Infektion, die sich durchs ganze System zieht.

  • Blocklisten sind wie Masken: Sie verhindern den direkten Eintritt.
  • Object.create(null) ist wie eine Impfung: Manche Objekte können gar nicht „vergiftet“ werden.
  • Monitoring ist wie Fiebermessen: Es erkennt, wenn etwas nicht stimmt.

Die beste Verteidigung: eine Kombination aus allen Maßnahmen.


Was du mitnehmen solltest

  • Gefährliche Keys blocken oder gar nicht erlauben.
  • Sichere Container nutzen: Object.create(null) oder Map.
  • Nur eigene Eigenschaften prüfen, niemals blind obj.key.
  • Parser und Merge-Funktionen absichern.
  • Tests und Monitoring einbauen, damit Pollution nicht unbemerkt bleibt.
  • Dependencies aktuell halten – viele NPM-Pakete hatten Pollution-Bugs.

Kommentare

Schreibe einen Kommentar

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