Prototype Pollution
Also Known As
JS prototype pollution
__proto__ attack
object prototype injection
TL;DR
An attacker injects properties into JavaScript's Object.prototype, affecting all objects in the application.
Explanation
Prototype pollution is a JavaScript-specific vulnerability where user-controlled keys like __proto__ or constructor.prototype are used to write properties onto Object.prototype. Because all JavaScript objects inherit from Object.prototype, injected properties propagate globally and can override application logic, bypass security checks, or escalate to remote code execution in server-side Node.js contexts. PHP is not affected, but PHP applications serving JavaScript-heavy frontends should sanitise JSON inputs and use libraries with known mitigations.
How It's Exploited
POST /api/merge Content-Type: application/json
{"__proto__": {"isAdmin": true}}
# All subsequent {} objects have isAdmin: true → auth bypass
{"__proto__": {"isAdmin": true}}
# All subsequent {} objects have isAdmin: true → auth bypass
Common Misconception
✗ Prototype pollution is only exploitable in the browser. Server-side JavaScript (Node.js) is equally vulnerable — polluting Object.prototype can affect all objects in the process, enabling DoS, property injection, and in some cases RCE via gadget chains.
Why It Matters
Polluting JavaScript's Object.prototype affects every object in the runtime — attackers can inject properties that alter application behaviour, bypass checks, or enable XSS.
Common Mistakes
- Deep-merging objects using recursive assignment without checking for __proto__ or constructor keys.
- Using user-supplied strings as keys in object assignment — an attacker sends __proto__[isAdmin]=true.
- Not using Object.create(null) for dictionaries/maps that accept user-controlled keys.
- Ignoring prototype pollution in server-side Node.js — it's not only a browser problem.
Code Examples
✗ Vulnerable
// JavaScript — merging user object pollutes Object.prototype
function merge(target, source) {
for (let key in source) {
target[key] = source[key]; // key could be '__proto__'
}
}
merge({}, JSON.parse('{"__proto__":{"admin":true}}'));
console.log({}.admin); // true — every new object is now 'admin'
✓ Fixed
// Use Object.keys() — own keys only, no prototype chain
function safeMerge(target, source) {
for (let key of Object.keys(source)) {
if (['__proto__','constructor','prototype'].includes(key)) continue;
target[key] = source[key];
}
}
// Or use Object.create(null) — no prototype at all
const safe = Object.create(null);
// Or JSON round-trip for deep clone of untrusted data:
const clone = JSON.parse(JSON.stringify(untrusted));
// PHP note: not applicable — PHP objects don't share a mutable prototype
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
22 Mar 2026
Views
24
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 1
No pings yesterday
Amazonbot 9
Google 4
Unknown AI 3
Perplexity 2
ChatGPT 2
Ahrefs 1
Also referenced
How they use it
crawler 16
crawler_json 4
pre-tracking 1
Related categories
⚡
DEV INTEL
Tools & Severity
🟠 High
⚙ Fix effort: Medium
⚡ Quick Fix
Never merge user-supplied objects into application objects with recursive merge; use Object.create(null) for hash maps; validate that __proto__ and constructor keys are absent
📦 Applies To
javascript ES5
web
cli
🔍 Detection Hints
Recursive object merge with user input: _.merge(target, userInput) or deep assign without __proto__ filtering
Auto-detectable:
✓ Yes
semgrep
eslint
snyk
⚠ Related Problems
🤖 AI Agent
Confidence: High
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: Function
Tests: Update
CWE-915
CWE-1321