Security
9 min read
2757 views

Prototype Pollution: Die JavaScript-Sicherheitslücke, die Ihre gesamte App vergiftet ☣️

IT
InstaTunnel Team
Published by our engineering team
Prototype Pollution: Die JavaScript-Sicherheitslücke, die Ihre gesamte App vergiftet ☣️

Im Bereich der Webanwendungssicherheit kündigen sich einige Schwachstellen mit dramatischen Abstürzen oder offensichtlichen Sicherheitsverletzungen an. Andere arbeiten still und heimlich, indem sie Ihre Anwendung von Grund auf beschädigen. Prototype pollution gehört zur letzteren Kategorie, eine subtile, aber verheerende Schwachstelle, die JavaScript’s prototypbasierte Vererbung ausnutzt, um jede Objekt in Ihrer Anwendung mit einer einzigen bösartigen Eingabe zu vergiften.

Das Verständnis der JavaScript-Prototype-Kette

Bevor wir in den Angriff eintauchen, müssen wir verstehen, wie JavaScript’s prototypbasierte Vererbung funktioniert. Anders als klassische objektorientierte Sprachen nutzt JavaScript Prototypen, um Eigenschaften und Methoden zwischen Objekten zu teilen. Jedes JavaScript-Objekt hat eine interne Verbindung zu einem anderen Objekt, genannt sein Prototype, wodurch die sogenannte Prototype-Kette entsteht.

Wenn Sie auf eine Eigenschaft eines Objekts zugreifen, prüft JavaScript zuerst, ob diese Eigenschaft direkt auf dem Objekt existiert. Falls nicht, wandert es die Prototype-Kette nach oben, prüft jeden Prototype, bis es entweder die Eigenschaft findet oder das Ende der Kette bei Object.prototype erreicht. Dieser elegante Mechanismus ermöglicht effizientes Teilen von Eigenschaften, schafft aber auch eine gefährliche Angriffsfläche.

Betrachten Sie diesen scheinbar harmlosen Code:

const user = { name: 'Alice' };
console.log(user.toString); // [Function: toString]

Obwohl wir keine toString-Eigenschaft auf dem user-Objekt definiert haben, findet JavaScript sie, indem es die Prototype-Kette bis zu Object.prototype durchläuft, wo toString definiert ist. Dieses Verhalten ist grundlegend für JavaScript, wird aber gefährlich, wenn Angreifer diese Prototypen manipulieren können.

Was ist Prototype Pollution?

Prototype pollution ist eine Schwachstelle, die es Angreifern ermöglicht, Eigenschaften in bestehende JavaScript-Prototypen einzuschleusen, indem sie die Tatsache ausnutzen, dass JavaScript die Modifikation aller Objektattribute erlaubt, einschließlich spezieller Eigenschaften wie __proto__, constructor und prototype.

Der Angriff funktioniert, indem er die Art und Weise ausnutzt, wie JavaScript die Zuweisung von Objekt-Eigenschaften handhabt. Wenn eine Anwendung Benutzereingaben in bestehende Objekte ohne ordnungsgemäße Validierung zusammenführt, kann ein Angreifer bösartige Eingaben erstellen, die den Basis-Object.prototype modifizieren. Da fast jedes Objekt in JavaScript von Object.prototype erbt, wirkt sich diese einzelne Änderung auf die gesamte Anwendung aus.

Solche Schwachstellen entstehen typischerweise, wenn JavaScript-Funktionen rekursiv Objekte mit benutzereingebbaren Eigenschaften zusammenführen, ohne die Schlüssel vorher zu bereinigen, insbesondere beim Verarbeiten von JSON-Daten aus unzuverlässigen Quellen.

Die Anatomie eines Prototype Pollution-Angriffs

Schauen wir uns an, wie ein realer Prototype Pollution-Angriff mit einer anfälligen Merge-Funktion funktioniert, ähnlich denen in beliebten Bibliotheken. Hier ein vereinfachtes Beispiel:

function merge(target, source) {
  for (let key in source) {
    if (typeof source[key] === 'object') {
      if (!target[key]) target[key] = {};
      merge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
  return target;
}

// Normale Verwendung
const user = {};
merge(user, { name: 'Alice', role: 'user' });
console.log(user); // { name: 'Alice', role: 'user' }

Diese Funktion erscheint harmlos. Sie führt rekursiv Eigenschaften eines Quellobjekts in ein Zielobjekt zusammen. Aber sehen Sie, was passiert, wenn ein Angreifer bösartige Eingaben liefert:

// Bösartige Nutzlast
const maliciousInput = JSON.parse('{"__proto__": {"isAdmin": true}}');

// Prototype vergiften
merge({}, maliciousInput);

// Jetzt hat JEDES Objekt die Eigenschaft isAdmin
const normalUser = {};
console.log(normalUser.isAdmin); // true - Vergiftung erfolgreich!

Der Angreifer nutzte die spezielle __proto__-Eigenschaft, um auf Object.prototype zuzugreifen und eine isAdmin-Eigenschaft einzuschleusen. Jetzt erben alle Objekte in der Anwendung, inklusive neu erstellter Objekte, die nie mit unserer Merge-Funktion in Berührung kamen, diese vergiftete Eigenschaft.

Auswirkungen in der Praxis: Die Lodash-Fallstudie

Die beliebte Lodash-Bibliothek ist von Prototype Pollution-Schwachstellen betroffen, insbesondere in Funktionen wie defaultsDeep, merge und mergeWith, die es böswilligen Nutzern ermöglichen, das Prototype von Object über __proto__ zu modifizieren, was potenziell Millionen von Anwendungen weltweit betrifft.

Betrachten wir diesen anfälligen Code, der Lodashs merge-Funktion nutzt:

const _ = require('lodash');
const express = require('express');
const app = express();
app.use(express.json());

app.post('/api/update-settings', (req, res) => {
  const userSettings = {};
  // Anfällig: Zusammenführung untrusted user input
  _.merge(userSettings, req.body);
  
  // Später in der Anwendung
  if (userSettings.isAdmin) {
    // Admin-Zugriff gewähren
    return res.json({ access: 'admin' });
  }
  res.json({ access: 'user' });
});

Ein Angreifer könnte diese Nutzlast senden:

{
  "__proto__": {
    "isAdmin": true
  }
}

Diese einzelne Anfrage vergiftet das Prototype, und plötzlich erben alle Objekte in der Anwendung, inklusive jener für Berechtigungsprüfungen, die isAdmin auf true gesetzt haben. Die Auswirkungen breiten sich in der gesamten Anwendung aus.

Von Vergiftung zu Remote Code Execution

Die Folgen von Prototype Pollution gehen weit über einfache Privilegieneskalation hinaus. In Node.js-Umgebungen können geschickte Angreifer Prototype Pollution mit anderen Schwachstellen kombinieren, um Remote Code Execution (RCE) zu erreichen. Dies geschieht durch eine Technik namens “Gadget Chains”, bei der vergiftete Eigenschaften mit bestehenden Codepfaden in unerwarteter Weise interagieren.

Beispielsweise kann eine Anwendung, die Kind-Prozesse startet und auf vererbte Eigenschaften bei der Befehlsbildung angewiesen ist, durch Prototype Pollution bösartige Befehle einschleusen:

// Anfälliger Code
const { spawn } = require('child_process');

function executeCommand(options) {
  const defaultOptions = {};
  // Optionen könnten vergiftete Eigenschaften erben
  const finalOptions = Object.assign(defaultOptions, options);
  
  spawn('node', [finalOptions.script || 'default.js'], {
    shell: finalOptions.shell || false,
    env: finalOptions.env || process.env
  });
}

Wenn ein Angreifer das Prototype mit {"shell": true, "script": "; malicious-command"} vergiftet, könnte er bei unzureichender Überprüfung der Eigenschaften Code ausführen.

Cross-Site Scripting durch Prototype Pollution

Angreifer können Prototypen mit Eigenschaften wie innerHTML, src oder onerror vergiften. Wenn die Anwendung diese Eigenschaften später referenziert und in den DOM einfügt, wird Cross-Site Scripting (XSS) möglich. Diese Variante ist besonders gefährlich in clientseitigen JavaScript-Frameworks:

// Anfälliges Template-Rendering
function renderContent(element, data) {
  element.innerHTML = data.content || 'Standardinhalt';
}

// Angreifer vergiftet den Prototype
const malicious = JSON.parse('{"__proto__": {"content": "<img src=x onerror=alert(document.cookie)>"}}');
merge({}, malicious);

// Später im Code
const emptyData = {};
renderContent(document.getElementById('output'), emptyData);
// XSS durch vergifteten Prototype ausgelöst!

Erkennung in der Praxis

Forscher, die fortschrittliche Fuzzing-Techniken verwenden, haben 65 neue Prototype Pollution-Schwachstellen in Zero-Day-Szenarien entdeckt, die mit herkömmlichen Methoden nicht erkannt werden konnten. Dies zeigt, wie verbreitet dieses Problem weiterhin ist. Das Problem bei Prototype Pollution ist seine Subtilität. Im Gegensatz zu SQL-Injection oder XSS, die oft sofortige Fehler oder sichtbare Effekte erzeugen, kann Prototype Pollution still in Ihrem Code verbleiben und auf die richtigen Bedingungen warten, um katastrophale Fehler zu verursachen.

Verteidigungsmaßnahmen: Fehlerresistente Anwendungen bauen

Der Schutz Ihrer Anwendung vor Prototype Pollution erfordert einen mehrschichtigen Ansatz, der sichere Programmierpraktiken, Eingabekontrolle und architektonische Entscheidungen kombiniert.

Verwendung von Object.create(null) für Wörterbücher

Objekte ohne Prototypen, erstellt mit Object.create(null), durchbrechen die Prototype-Kette und verhindern Vergiftungen. Dies ist eine der effektivsten Verteidigungsmaßnahmen:

// Sicher: Kein Prototyp
const userSettings = Object.create(null);
userSettings.name = 'Alice';

// Kann nicht vergiftet werden
const malicious = JSON.parse('{"__proto__": {"isAdmin": true}}');
Object.assign(userSettings, malicious);
console.log(userSettings.isAdmin); // undefined - geschützt!

Objekte, die auf diese Weise erstellt wurden, haben keinen Prototyp und sind somit immun gegen Prototype Pollution. Verwenden Sie dieses Muster für alle Objekte, die benutzereingebbare Daten enthalten.

Implementierung von Property-Validierung und -Sanitization

Validieren und säubern Sie immer Objekt-Schlüssel, bevor Sie Benutzereingaben verarbeiten:

function secureMerge(target, source) {
  const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
  
  for (let key in source) {
    // Gefährliche Schlüssel blockieren
    if (dangerousKeys.includes(key)) {
      continue;
    }
    // Zusätzliche Validierung
    if (typeof key !== 'string' || key.startsWith('_')) {
      continue;
    }
    if (typeof source[key] === 'object' && source[key] !== null) {
      if (!target[key]) target[key] = Object.create(null);
      secureMerge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
  return target;
}

Verwendung von Object.freeze() für kritische Objekte

Frieren Sie Prototypen und kritische Objekte ein, um Änderungen zu verhindern:

// Verhindert Prototype Pollution auf der Root-Ebene
Object.freeze(Object.prototype);
Object.freeze(Object);

// Konfigurationsobjekte einfrieren
const config = Object.freeze({
  apiUrl: 'https://api.example.com',
  timeout: 5000
});

Diese Methode ist effektiv, kann aber legitimen Code, der auf Prototyp-Änderungen angewiesen ist, beeinträchtigen. Verwenden Sie sie selektiv für sicherheitskritische Objekte.

Verwendung von Map anstelle von Plain Objects

Als Best Practice verwenden Sie Map anstelle von Object zum Speichern von Schlüssel-Wert-Paaren, da Maps nicht von Object.prototype erben:

// Sichere Alternative zu Objekten
const userSettings = new Map();
userSettings.set('name', 'Alice');
userSettings.set('role', 'user');

// Nicht anfällig für Prototype Pollution
console.log(userSettings.get('isAdmin')); // undefined

Maps bieten eine sauberere API für Schlüssel-Wert-Speicherung und eliminieren das Risiko der Prototype Pollution vollständig.

Schema-Validierung implementieren

Verwenden Sie Schema-Validierungsbibliotheken, um strikte Objektstrukturen durchzusetzen:

const Joi = require('joi');

const userSchema = Joi.object({
  name: Joi.string().required(),
  email: Joi.string().email().required(),
  role: Joi.string().valid('user', 'admin').required()
}).unknown(false); // Unbekannte Eigenschaften ablehnen

app.post('/api/users', (req, res) => {
  const { error, value } = userSchema.validate(req.body);
  if (error) {
    return res.status(400).json({ error: error.details });
  }
  // Sicher, um die validierten Daten zu verwenden
  processUser(value);
});

Schema-Validierung stellt sicher, dass nur erwartete Eigenschaften an die Anwendungslogik gelangen, und blockiert Prototype Pollution bei der Eingabe.

Abhängigkeiten regelmäßig aktualisieren

Halten Sie Ihre Abhängigkeiten aktuell, insbesondere sicherheitskritische Bibliotheken. Moderne Versionen beliebter Bibliotheken wie Lodash haben Prototype Pollution-Schwachstellen behoben, vorausgesetzt, Sie verwenden aktuelle Versionen. Nutzen Sie Tools wie npm audit oder Snyk, um anfällige Abhängigkeiten zu identifizieren:

npm audit
npm audit fix

Strikte Modi aktivieren

Der JavaScript-Striktemodus bietet zusätzlichen Schutz:

'use strict';

// Strikter Modus verhindert versehentliche globale Variablenerstellung
// und lässt einige stille Fehler zu Ausnahmen werden
function processData(input) {
  // Sicherere Ausführungsumgebung
}

Tests auf Prototype Pollution

Fügen Sie Tests auf Prototype Pollution in Ihre Sicherheits-Test-Suite ein:

describe('Prototype Pollution Tests', () => {
  it('sollte Object.prototype nicht vergiften', () => {
    const original = Object.prototype.toString;
    // Versuch der Vergiftung
    const malicious = { "__proto__": { "isAdmin": true } };
    merge({}, malicious);
    // Keine Vergiftung überprüfen
    const testObj = {};
    expect(testObj.isAdmin).toBeUndefined();
    expect(Object.prototype.toString).toBe(original);
  });
  
  it('sollte __proto__ in JSON-Eingaben ablehnen', () => {
    const input = '{"__proto__": {"polluted": true}}';
    const result = safeJSONParse(input);
    expect({}.polluted).toBeUndefined();
  });
});

Das große Ganze: Sicher von Haus aus

Prototype Pollution ist ein Beispiel für ein grundlegendes Prinzip in der Anwendungssicherheit: Verteidigung in der Tiefe. Kein einzelner Ansatz eliminiert das Risiko vollständig. Stattdessen sollten mehrere Verteidigungsstrategien kombiniert werden:

  1. Verwendung von prototype-freien Objekten (Object.create(null)) für Benutzerdaten
  2. Validierung und Sanitisierung aller Eingaben, insbesondere Objekt-Schlüssel
  3. Einfrieren kritischer Objekte und Prototypen
  4. Verwendung von Map statt Plain Objects für Wörterbücher
  5. Implementierung von strikter Schema-Validierung
  6. Regelmäßige Aktualisierung der Abhängigkeiten
  7. Durchführung regelmäßiger Sicherheitstests
  8. Überwachung auf ungewöhnliche Property-Access-Muster in der Produktion

Fazit

Prototype Pollution zeigt, wie eine scheinbar harmlose Sprachfunktion zu einer ernsthaften Sicherheitslücke werden kann, wenn sie mit untrusted input genutzt wird. Durch die Ausnutzung der JavaScript-Prototype-Kette können Angreifer Eigenschaften einschleusen, die jede Objekt in Ihrer Anwendung vergiften, was zu Privilegieneskalation, Denial of Service oder sogar Remote Code Execution führen kann.

Die gute Nachricht ist, dass Prototype Pollution durch diszipliniertes Programmieren verhindert werden kann. Wenn Sie die Angriffsvektoren verstehen, robuste Eingabevalidierung implementieren, sicherere Objekt-Erstellungsmuster verwenden und Ihre Abhängigkeiten aktuell halten, können Sie JavaScript-Anwendungen bauen, die diese subtile, aber verheerende Schwachstelle widerstehen.

Denken Sie daran: In JavaScript ist Ihre Anwendung nur so sicher wie ihre Prototype-Kette. Eine vergiftete Prototype kann Ihre gesamte App vergiften, aber mit den richtigen Verteidigungsstrategien können Sie sicherstellen, dass Ihre Objekte rein bleiben und Ihre Anwendung sicher bleibt.

Continue from this article into the most relevant product guides and workflows.

Related Topics

#prototype pollution, JavaScript security vulnerability, prototype chain attack, Object.prototype pollution, proto vulnerability, JavaScript exploit, web application security, Node.js security, Lodash vulnerability, prototype pollution prevention, Object.create null, JavaScript security best practices, prototype pollution attack, CVE JavaScript, secure coding JavaScript, JavaScript RCE, prototype pollution XSS, npm security, JavaScript input validation, Object.freeze security, prototype pollution defense, JavaScript malicious payload, dependency vulnerability, prototype-based inheritance, JavaScript security patterns, web security 2025, OWASP JavaScript, prototype pollution detection, secure merge function, JavaScript sanitization, Map vs Object security, prototype pollution testing, JavaScript security audit, client-side security, server-side JavaScript security, prototype chain exploitation, JavaScript remote code execution, privilege escalation JavaScript, gadget chain attack, JSON security, untrusted input handling, JavaScript prototype manipulation, secure object creation, JavaScript framework security, supply chain security, npm audit, Snyk security, prototype pollution mitigation, defensive programming JavaScript, zero-day JavaScript vulnerability, JavaScript security research, prototype pollution fuzzing, strict mode JavaScript, schema validation security, Joi validation, Express.js security, API security JavaScript, JavaScript security testing, secure development lifecycle, JavaScript threat model, prototype pollution impact, DOM-based XSS, JavaScript injection attack, secure JavaScript patterns, modern JavaScript security, TypeScript security, JavaScript security tools, penetration testing JavaScript, security code review, JavaScript vulnerability assessment, prototype pollution remediation, secure coding standards, JavaScript SAST, dynamic analysis JavaScript, JavaScript security checklist, OWASP Top 10 JavaScript, secure SDLC, JavaScript hardening, runtime protection JavaScript, JavaScript WAF bypass, security automation JavaScript, DevSecOps JavaScript, shift-left security, JavaScript security training, secure JavaScript development, prototype pollution case study, real-world JavaScript attacks, JavaScript security trends 2025, enterprise JavaScript security, microservices security, API gateway security, serverless security JavaScript, cloud security JavaScript, container security Node.js, Kubernetes security JavaScript, CI/CD security, GitHub security scanning, npm package security, open source security, software composition analysis, JavaScript security monitoring, application security testing, bug bounty JavaScript, responsible disclosure, CVE database, NVD JavaScript, security advisory, patch management JavaScript, version control security, code signing, JavaScript obfuscation security, minification security, webpack security, bundler security, frontend security, backend security, full-stack security JavaScript, React security, Vue security, Angular security, Next.js security, Express security, Fastify security, Koa security, JavaScript framework vulnerabilities

Keep building with InstaTunnel

Read the docs for implementation details or compare plans before you ship.

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles