Prototype Pollution

Attacker-controlled data used to set a property on Object.prototype itself, affecting every plain object in the program — distinct from DOM XSS.

Purpose

Prototype pollution happens when attacker-controlled data walks into a merge, assign, or clone operation that doesn’t filter special keys — reaching and mutating Object.prototype itself, which every plain object in the program inherits from.

Example

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

const malicious = JSON.parse('{"__proto__": {"isAdmin": true}}');
merge({}, malicious);

({}).isAdmin; // true — every plain object now has this, program-wide

Behavior notes

  • The attack works by walking __proto__ as an ordinary enumerable key — most naive merge/deep-clone implementations don’t special-case it.
  • The impact depends entirely on what the rest of the application does with the polluted prototype — it doesn’t execute code directly, but can corrupt logic that checks a property assumed to be absent (if (user.isAdmin)) anywhere in the program.
  • Object.create(null) or a Map avoids the risk entirely for genuinely untrusted key-value data, since neither has a prototype chain a __proto__ key could reach.

Common mistakes

  • Assuming input validation aimed at DOM XSS (escaping HTML) also protects against prototype pollution — they’re unrelated risk classes requiring separate defenses.
  • Writing a custom merge/deep-clone utility without an explicit denylist for __proto__, constructor, and prototype.

Runtime notes

Applies to any JavaScript environment with the standard prototype-chain object model — browser and Node code are both affected equally.

Taught in these lessons

Related reference

Updated 2026-09-03