flawopen.com/Vulnerabilities/JavaScript Prototype Pollution
Imagine a factory that manufactures toy robots. Every robot inherits its default settings (e.g. 'Can this robot fire lasers? No') from a single master blueprint in the supervisor's office. A sneaky visitor writes on the master blueprint: 'Can this robot fire lasers? YES'. Suddenly, every single robot built in the factory—past, present, and future—inherits the ability to fire lasers! In JavaScript Prototype Pollution, an attacker modifies Object.prototype, altering default properties across every single object in the entire Node.js server.
__proto__, constructor.prototypeisAdmin: true), Remote Code ExecutionObject.create(null), Map data structuresJavaScript objects inherit properties from prototype objects via the prototype chain. When an application recursively copies untrusted user objects into existing configurations without filtering property keys, an attacker supplies properties named __proto__ or constructor.prototype. The recursive assignment mutates the base Object.prototype, causing all newly created objects to inherit the attacker's values.
// VULNERABLE: Recursive merge without prototype key filtering
function merge(target, source) {
for (let key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
if (!target[key]) target[key] = {};
merge(target[key], source[key]); // Recursively copies __proto__!
} else {
target[key] = source[key];
}
}
return target;
}
// Attacker sends: JSON.parse('{"__proto__": {"isAdmin": true}}')
// HARDENED: Disallow dangerous prototype chain keys
function mergeSafe(target, source) {
const BLOCKED_KEYS = ['__proto__', 'constructor', 'prototype'];
for (let key of Object.keys(source)) {
if (BLOCKED_KEYS.includes(key)) {
continue; // Strictly discard prototype mutation attempts
}
if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
if (!target[key]) target[key] = {};
mergeSafe(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
{"__proto__": {"admin": true}}.target['__proto__']['admin'] = true mutates the global Object.prototype.:if (req.user.admin); because req.user inherits from Object.prototype, the check resolves to true, bypassing authentication.:__proto__, constructor, and prototype keys when deep merging or parsing objects.Object.create(null), which creates a pure key-value map with no prototype chain.new Map() instead of plain objects when storing user-supplied keys to eliminate prototype inheritance entirely.Object.freeze(Object.prototype) at process boot in Node.js to make prototypes immutable.