Securitycritical

Prototype Pollution

Prototype pollution is a vulnerability where attacker-controlled input modifies Object.prototype, affecting all objects in the application. It occurs when code recursively merges or assigns properties from user input without sanitizing keys like __proto__ or constructor.

Memory anchor

Prototype pollution = someone sneaking into the city water supply (__proto__) and adding poison. Every faucet (object) in town now dispenses poisoned water ({isAdmin: true}). The fix: use bottled water (Object.create(null)) or lock the water plant (Object.freeze(Object.prototype)).

Expected depth

An attacker sends JSON like `{"__proto__": {"isAdmin": true}}`. A vulnerable merge function assigns this to the target object's __proto__, adding isAdmin to Object.prototype. Now every plain object in the process has isAdmin: true. This can bypass authorization checks, enable path traversal (if a property like 'outputFunctionName' is read from Object.prototype in a template engine), or cause DoS via property injection into security-critical code paths.

Deep — senior internals

Prototype pollution has led to RCEs in popular packages: lodash (CVE-2019-10744), jQuery, hoek. Mitigations: (1) Use Object.create(null) for dictionaries to create prototype-less objects. (2) Validate and sanitize input keys—reject __proto__, constructor, prototype. (3) Use JSON.parse with a reviver that blocks these keys. (4) Use structuredClone() (Node.js 17+) for deep cloning (it doesn't copy prototype chains). (5) Enable --frozen-intrinsics experimental flag to freeze built-in prototypes. (6) Object.freeze(Object.prototype) at startup. (7) Use Map instead of plain objects for key-value stores. Detection: run npm audit and use static analysis tools like Snyk.

🎤Interview-ready answer

Prototype pollution happens when user-controlled JSON with __proto__ or constructor keys is recursively merged into objects, poisoning Object.prototype globally. Mitigations: validate input keys (reject __proto__, constructor, prototype), use Object.create(null) for dictionaries, use Map for key-value stores, and consider Object.freeze(Object.prototype) at startup. npm audit and Snyk catch vulnerable dependency versions.

Common trap

Object.assign() does not cause prototype pollution because it uses ownPropertyDescriptors—__proto__ is not an own property but an accessor on Object.prototype. Prototype pollution requires recursive merge (like _.merge). However, `JSON.parse('{"__proto__": {"x": 1}}')` does create an object with an own __proto__ property, which when assigned via a recursive merge does pollute. The distinction between JSON parse behavior and assignment behavior trips up engineers.

Related concepts