← All cybersecurity posts

Prototype Pollution in JavaScript: Client and Server Side

Prototype pollution is a JavaScript-specific vulnerability. By injecting properties into Object.prototype, an attacker can change the behaviour of every object in the application, sometimes with dramatic results.

A quick refresher on prototypes

In JavaScript, objects inherit properties from their prototype. Nearly every object ultimately inherits from Object.prototype. If you add a property there, every object appears to have it:

Object.prototype.isAdmin = true;
const user = {};
console.log(user.isAdmin); // true

How pollution happens

The usual cause is a function that merges, clones or sets nested properties from user-controlled input without blocking special keys:

// Vulnerable deep merge
function merge(target, source) {
  for (const key in source) {
    if (typeof source[key] === "object") merge(target[key] ??= {}, source[key]);
    else target[key] = source[key];
  }
}
merge({}, JSON.parse('{"__proto__": {"isAdmin": true}}'));

Keys like __proto__ and constructor.prototype let the input reach Object.prototype.

Other sources include query string parsers that support nested objects, such as ?__proto__[isAdmin]=true, and path-based setters like set(obj, "a.b.c", value).

Impact on the server side

In Node.js, polluted properties can affect application logic and libraries:

  • Privilege escalation: checks like if (user.isAdmin) become true for everyone.
  • Denial of service: polluting properties that break core functions.
  • Remote code execution: through "gadgets" in libraries, for example polluting options used when spawning child processes or rendering templates.

Impact on the client side

In the browser, pollution can combine with script gadgets in libraries to produce DOM XSS, for example by polluting a property a library later uses as HTML or a script URL.

How to prevent prototype pollution

  1. Block dangerous keys in merge, clone and set functions: __proto__, constructor and prototype.
  2. Use safe data structures: Map for user-controlled keys, or objects created with Object.create(null), which have no prototype.
  3. Validate input with a schema, rejecting unexpected keys.
  4. Keep libraries updated; many popular utilities have patched pollution bugs.
  5. Freeze the prototype in hardened environments with Object.freeze(Object.prototype), testing for compatibility.
  6. Use Node.js --disable-proto options where suitable, to remove the __proto__ accessor.
const BLOCKED = new Set(["__proto__", "constructor", "prototype"]);
function safeSet(obj, key, value) {
  if (BLOCKED.has(key)) return;
  obj[key] = value;
}

Testing for prototype pollution

Testing sends payloads using __proto__ and constructor.prototype in JSON bodies and query strings, then looks for changed behaviour. SecurityInfinity agents test for pollution sources and flag vulnerable dependencies through static analysis. Start free.

Frequently asked questions

Is prototype pollution only a Node.js problem?

It affects JavaScript in both browsers and Node.js. Other languages have related issues but not this exact mechanism.

Does using TypeScript prevent it?

No. TypeScript types are removed at runtime and do not stop malicious keys in parsed input.

How serious is prototype pollution?

It ranges from low to critical depending on available gadgets. Server-side pollution leading to RCE is critical.

Find what attackers can see, before they do.

AI pentesting agents test your web app, API and cloud, and every finding comes with evidence and a fix. First report in about 10 minutes.

Stay ahead of attackers.

New vulnerabilities, breach write-ups and practical fixes. A few emails a month.