Prototype pollution is a type of vulnerability that affects JavaScript applications, often found in Node.js (npm) packages. This vulnerability allows an attacker to inject properties into an object’s prototype, potentially leading to unexpected behavior and security issues. Here’s how it works and how it can be exploited:
How Prototype Pollution Works
JavaScript Prototypes:
In JavaScript, objects can inherit properties and methods from their prototype. Every JavaScript object has a prototype, and objects can be created with the Object.create
method, inheriting the properties from a prototype object.
Manipulating the Prototype Chain:
Prototype pollution occurs when an attacker can modify the prototype of built-in objects like Object
, Array
, etc. By injecting properties into the prototype, the attacker can affect all objects that inherit from that prototype.
Vulnerable Code Patterns:
Prototype pollution typically happens due to improper handling of user input when merging objects, assigning properties, or copying objects. Commonly used utility functions like merge
, extend
, or similar can be vulnerable if they do not validate input properly.
Example of Prototype Pollution
Consider the following code that merges two objects:
const merge = (target, source) => {
for (let key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
return target;
};
let a = {};
let b = JSON.parse('{"__proto__":{"polluted":"yes"}}');
merge(a, b);
console.log({}.polluted); // Outputs: "yes"
In above example:
- The
merge
function copies properties from the source objectb
to the target objecta
. - The input JSON for
b
includes a__proto__
property, which is a reference to the prototype of all objects. - When
merge
function processesb
, it adds thepolluted
property to the prototype of all objects. - As a result, all objects now have a
polluted
property with the value “yes”.
When she reached the first hills of the Italic Mountains, she had a last view back on the skyline of her hometown Bookmarksgrove, the headline of Alphabet Village and the subline of her own road, the Line Lane.
Mitigating Prototype Pollution
To prevent prototype pollution, you can take the following measures:
Validate Input:
Ensure that user-supplied data does not contain keys like __proto__
, constructor
, and prototype
.
Use Safe Methods:
Use libraries and methods that handle object merging safely. Many modern libraries like lodash
have addressed this issue in their implementations.
Avoid Prototype Manipulation:
Avoid using code that directly manipulates prototypes unless absolutely necessary and understand the security implications.
Security Testing:
Regularly perform security testing, including static analysis and dynamic analysis, to identify and remediate vulnerabilities in your codebase.
Example of a Safe Merge Function
Here’s an example of a safer merge function that avoids prototype pollution:
const safeMerge = (target, source) => {
for (let key in source) {
if (source.hasOwnProperty(key) && key !== '__proto__') {
target[key] = source[key];
}
}
return target;
};
let a = {};
let b = JSON.parse('{"__proto__":{"polluted":"yes"}}');
safeMerge(a, b);
console.log({}.polluted); // Outputs: undefined
In this modified function:
The if
condition checks that the key is not __proto__
before copying it to the target object, thus preventing prototype pollution.
By understanding and applying these principles, you can protect your applications from prototype pollution vulnerabilities.
we can extend the vulnerable example to include an XSS payload to demonstrate how prototype pollution can be used to inject malicious code. Here, I’ll modify the vulnerable example to include an XSS payload and create a simple proof of concept.
Step 1: Modify the Vulnerable Example to Include XSS
Modify vulnerable/index.js
:
// vulnerable/index.js
const express = require('express');
const app = express();
const merge = (target, source) => {
for (let key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
return target;
};
// Vulnerable route
app.get('/merge', (req, res) => {
let target = {};
let source = JSON.parse(req.query.source);
merge(target, source);
// Injecting the polluted property directly into the HTML response
res.send(`<div>${target.xss || ''}</div>`);
});
// Start the server
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
Update vulnerable/package.json
to include express:
// vulnerable/package.json
{
"name": "vulnerable",
"version": "1.0.0",
"description": "A vulnerable example of prototype pollution",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2"
}
}
Step 3: Proof of Concept for Prototype Pollution Leading to XSS
Open a web browser and navigate to:
http://localhost:3000/merge?source={"__proto__":{"xss":"<script>alert('XSS')</script>"}}

Navigate to:
http://localhost:3000/merge?source={"x":"y"}
The injected XSS payload (<script>alert('XSS')</script>
) will be included in the response due to prototype pollution, demonstrating the vulnerability.
Explanation
- The vulnerable
merge
function allows the prototype of objects to be polluted with arbitrary properties. - By passing
{"__proto__":{"xss":"<script>alert('XSS')</script>"}}
as a query parameter, we can inject thexss
property into the prototype of all objects. - When accessing any object property that doesn’t exist directly on the object, it will check the prototype chain and find the
xss
property. - The second request to
/merge?source={"x":"y"}
then includes the polluted prototype, which results in the<script>alert('XSS')</script>
being executed.
There is no universally accepted definition of a mountain. Elevation, volume, relief, steepness, spacing and continuity have been used as criteria for defining a mountain.