Prototype pollution is a JavaScript flaw where untrusted input sets properties on a shared prototype, usually Object.prototype, so every ordinary object in the process inherits them. It appears in code that merges, clones or assigns nested keys from input without blocking special keys.
How does the prototype chain make this possible?
When you read a property, JavaScript checks the object's own properties first, then its prototype, and so on until it finds the property or reaches null. MDN's example shows an object whose own property b shadows a prototype's b, while c is found on the prototype.
Ordinary object literals have Object.prototype as their prototype, which is why every object inherits methods like toString. Change something on Object.prototype and every ordinary object sees it.
The special key is __proto__. MDN says the object literal form is standard, and that the obj.proto accessor is non-standard and deprecated in favour of getPrototypeOf and setPrototypeOf. OWASP also names constructor.prototype as another path.
What does a constructed example look like?
Imagine a settings endpoint that deep merges a JSON body into a defaults object. A recursive merge function walks each key of the input. If the body has a key named __proto__ holding an object, the walk lands on the prototype of the target and copies the nested keys onto it, not onto the target.
Now suppose a request sets an isAdmin property that way. Later, an unrelated check reads user.isAdmin on an object that never defined it. The lookup walks the chain, finds the polluted value on Object.prototype, and the check passes. The attacker never touched the user object.
OWASP lists the possible results as unauthorized data access, privilege escalation and remote code execution. The cheat sheet does not detail the mechanism, so treat the walk-through above as an illustration of the idea, not a recipe for any specific library.
What should you look for in code review?
Flag these patterns:
- Recursive merge, extend, clone or set-by-path helpers that take keys from input.
- Code that copies user supplied JSON into plain objects with no key check.
- Property reads that assume a key is absent because it is undefined, on objects that may inherit.
- Use of
obj.hasOwnProperty(key)on objects that might be created without a prototype. MDN recommendsObject.hasOwn(obj, key). - Any path that assigns to
obj[a][b]where a and b both come from input.
Also check the versions of utility libraries you use. Merge and clone helpers are the usual home of this flaw, so dependency scanning belongs in the same review.
How do you prevent it?
OWASP's prevention list:
- Use Map or Set instead of object literals for keys and values from input.
- Create objects with Object.create(null), which do not inherit from Object.prototype. Use
{__proto__: null}only as a last resort. - Freeze built-in prototypes with Object.freeze. Freezing is shallow, and libraries that modify built-in prototypes can break, so test first.
- Do not rely on Object.seal, since existing values stay writable.
- Run Node.js with
--disable-proto=deleteas defense in depth. Pollution throughconstructor.prototyperemains possible.
The OWASP page we read did not cover schema validation or key filtering, so we make no claims about those from it, though rejecting unexpected keys at the boundary is a common review ask worth checking against your framework docs.
What to do
- Search the code for recursive merge and path-assign helpers and trace where their keys come from.
- Reject or strip
__proto__,constructorandprototypekeys at the input boundary. - Prefer Map for dynamic keys.
- Use Object.hasOwn for own-property checks.
- Add a test that sends a body with a
__proto__key and asserts that a fresh object is unchanged. - Update helper libraries and scan your lock file for known prototype pollution advisories.
- Consider the Node.js flag for production as an extra layer.
Safeguard's dependency scanning and reachability analysis can show whether a vulnerable merge helper in your tree is actually called from your code.
Quick check
Does Object.seal stop prototype pollution?
No. OWASP says sealing blocks adding or removing properties, but existing writable values can still change.
Why is Object.create(null) helpful?
Such objects have no prototype, so they do not inherit from Object.prototype and lookups cannot reach polluted values through it.
Sources
- MDN: Inheritance and the prototype chain (fetched 18 October 2026)
- OWASP Prototype Pollution Prevention Cheat Sheet (fetched 18 October 2026)
Last reviewed: 18 October 2026.