Prototype pollution is a JavaScript-specific vulnerability where attacker-controlled input modifies the prototype of base objects (Object, Array). Once polluted, every object inherits attacker-controlled properties β leading to RCE in some Node.js apps, XSS in some browsers, authentication bypass in some apps. This module covers the mechanism, detection, and defenses.
JavaScript prototype basics
Every object in JavaScript has a prototype. Property lookups walk up the chain: object β its prototype β its prototype’s prototype β … β Object.prototype β null.
const a = {};
console.log(a.toString); // function (inherited from Object.prototype)
Object.prototype.foo = "polluted";
console.log({}.foo); // "polluted" β every new object has it
If attacker can write to Object.prototype, every object in the application gets new properties, retroactively.
How pollution happens
Common vulnerable pattern: deep-merge functions, Object.assign loops, query-string parsers that nest, JSON parsers that allow __proto__.
// Vulnerable deep merge (simplified)
function merge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object') {
if (!target[key]) target[key] = {};
merge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
const userInput = JSON.parse('{"__proto__":{"isAdmin":true}}');
merge({}, userInput);
// Now: ({}).isAdmin === true for ANY object created afterwards
Attack vectors
Authentication bypass
// App checks user.isAdmin somewhere
if (user.isAdmin) { /* admin actions */ }
// Pollute Object.prototype.isAdmin = true β every user is admin
Property injection on config objects
Apps initialize config from defaults, then merge user options. Pollution introduces unwanted config β e.g., cors.origin = "*", session.secret = "known".
Continue reading with Pro tier (βΉ4,999/year)
You've read 29% of this module. Unlock the remaining deep-dive, quiz, and every other Advanced/Expert module.