JavaScript Prototype Pollution
What Is This Vulnerability?
Prototype pollution is a vulnerability specific to JavaScript where an attacker can inject properties into Object.prototype or other built-in prototypes. Since all objects in JavaScript inherit from Object.prototype, a polluted property becomes available on every object in the application, potentially altering logic, bypassing security checks, or enabling remote code execution.
Why It Happens
This vulnerability arises when applications use recursive merge, deep clone, or object extension functions that do not guard against keys like __proto__, constructor, or prototype. User-controlled JSON payloads processed by these unsafe functions can modify the prototype chain of all objects in the process.
Example Code
function deepMerge(target: any, source: any) {
for (const key of Object.keys(source)) {
if (typeof source[key] === "object" && source[key] !== null) {
if (!target[key]) target[key] = {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
app.post("/settings", (req, res) => {
const userSettings = deepMerge({}, req.body);
res.json(userSettings);
});function deepMerge(target: any, source: any) {
for (const key of Object.keys(source)) {
if (key === "__proto__" || key === "constructor" || key === "prototype") {
continue;
}
if (typeof source[key] === "object" && source[key] !== null) {
if (!target[key]) target[key] = {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
app.post("/settings", (req, res) => {
const userSettings = deepMerge(Object.create(null), req.body);
res.json(userSettings);
});How Hackers Exploit It
An attacker sends a JSON payload like {"__proto__": {"isAdmin": true}}. If the server uses an unsafe merge function, Object.prototype.isAdmin is set to true. Every subsequent object in the application that checks obj.isAdmin will find it to be true, potentially granting admin access. In some cases, prototype pollution chains with template engines or child_process can lead to remote code execution.
How to Fix It
Use Object.create(null) for lookup maps to avoid prototype inheritance. In merge and clone functions, skip keys named __proto__, constructor, and prototype. Prefer well-maintained libraries like lodash (which patched this in v4.17.12+) over custom deep-merge implementations. Freeze Object.prototype in test environments to detect pollution. Use Map instead of plain objects for user-controlled key-value stores.