flawopen.com/Vulnerabilities/JavaScript Prototype Pollution
JavaScript Prototype Pollution
Imagine a workshop where each new device checks a shared rulebook only when its own settings do not answer a question. If an unsafe parts clerk writes an attacker-controlled rule into that book, unrelated devices may read it as a default. Prototype pollution is the software version: unsafe dynamic property writes can alter an object's prototype, sometimes Object.prototype. The effect is not automatically global, and impact requires application code that later trusts the inherited property.
__proto__ or a constructor.prototype path reaching an unsafe writeMap or null-prototype dictionaries for untrusted keysCore Concepts & Subsystem Terms
- Object.prototype
- The shared prototype inherited by ordinary objects. Pollution of this object can affect many objects, but not every JavaScript object or every runtime realm.
- __proto__
- A legacy accessor on ordinary objects that gets or sets their prototype. In JSON text, the same spelling is an ordinary property name until later code handles it unsafely.
- Recursive Deep Merge
- A copier that walks nested keys and writes them into another object. It becomes dangerous when untrusted keys can select prototype-related properties or unsafe paths.
- CWE-1321
- Improperly Controlled Modification of Object Prototype Attributes. The weakness is the uncontrolled prototype mutation; downstream impact depends on reachable application behavior.
Step-by-Step Attack Flow
Untrusted Property Input
An endpoint accepts attacker-controlled object keys. Parsing JSON with an own __proto__ property alone does not mutate a prototype.
Unsafe Dynamic Write
A recursive merge or path setter follows those keys and performs dynamic writes without validating each path segment.
Target or Shared Prototype Changes
Depending on the target and write sequence, the operation changes a target's prototype or reaches a shared prototype such as Object.prototype. These outcomes are distinct.
A Gadget Determines Impact
A later gadget reads the inherited value in a security-sensitive operation. The result depends on that gadget; authentication bypass or code execution must not be assumed.
The Mechanism & Root Cause
JavaScript property lookup follows an object's prototype chain. A vulnerable merge or path setter can let untrusted keys such as __proto__ or constructor.prototype steer a write into a prototype. An own __proto__ field produced by JSON.parse is not itself pollution; later code must interpret it through an unsafe assignment. A changed target prototype affects that target, while modifying Object.prototype affects ordinary objects that inherit from it. A separate gadget must consume the inherited property before there is security impact.
// VULNERABLE: attacker-controlled keys reach recursive writes
function merge(target, source) {
for (const key in source) {
const value = source[key];
if (value && typeof value === 'object') {
if (!target[key]) target[key] = {};
merge(target[key], value);
} else {
target[key] = value;
}
}
return target;
}
// JSON.parse creates an own __proto__ data property; the unsafe merge is the risk.
const input = JSON.parse('{"__proto__": {"isAdmin": true}}');
merge({}, input);
// HARDENED: copy only fields this endpoint intentionally accepts
const PROFILE_KEYS = new Set(['displayName', 'theme']);
function parseProfile(input) {
if (!input || typeof input !== 'object' || Array.isArray(input)) {
throw new TypeError('profile must be an object');
}
const profile = Object.create(null);
for (const key of Object.keys(input)) {
if (!PROFILE_KEYS.has(key)) continue;
const value = input[key];
if (typeof value !== 'string' || value.length > 80) {
throw new TypeError('invalid profile field');
}
profile[key] = value;
}
return profile;
}
The Attack & Exploit Sequence
- 1Input: a request includes an unexpected object key such as
{"__proto__": {"admin": true}}. JSON parsing alone does not mutate a prototype. - 2Unsafe write: a generic merge or path setter processes the key without an allowlist.
- 3Prototype reachability: the particular write may change a target's prototype or reach a shared prototype, depending on the code path.
- 4Impact gadget: only if later code trusts an inherited property for a sensitive decision can the pollution affect that decision. Test the actual gadget and authorization path.
Defensive Engineering & Prevention Rules
- ✓Prefer schema allowlists: Build the destination from fields the endpoint is meant to accept. If a generic path writer is unavoidable, reject prototype-related path segments at every nesting level.
- ✓Use safe dictionaries: Store arbitrary user keys in
Mapor anObject.create(null)dictionary; validate before handing data to APIs expecting ordinary objects. - ✓Check own properties: Use
Object.hasOwn(record, key)for security-sensitive presence checks; do not let inherited defaults authorize actions. - ✓Use runtime hardening as defense in depth: Node.js
--disable-proto=throwremoves one attack path but does not blockconstructor.prototype. Freezing built-ins can break polyfills and dependencies, so assess compatibility and never treat it as a substitute for safe writes.
FAQ
No. JSON.parse() creates an own data property named __proto__; it does not invoke the inherited setter. Risk arises if a later merge or assignment operation invokes that setter. See whether JSON.parse() causes prototype pollution.
It can prevent writes to the frozen Object.prototype itself, but it does not stop a setter from changing an individual object’s prototype or protect every other prototype. It may also break code that extends built-ins. Use it only as defense in depth, not as the data-handling fix.
No. TypeScript types are erased at runtime. A value declared as Record<string, string> can still contain a __proto__ key at runtime; validate untrusted keys and use safe object construction.
- MDN — JavaScript prototype pollution
- OWASP — Prototype Pollution Prevention Cheat Sheet
- CWE-1321 — Improperly Controlled Modification of Object Prototype Attributes
- MDN — Object.prototype.__proto__ accessor
- Is Object.assign() safe from prototype pollution?