JavaScript is a powerful and versatile programming language, commonly used for both client-side and server-side development. However, with this versatility comes certain vulnerabilities, one of the most notable being Prototype Pollution. This vulnerability arises from the way JavaScript objects handle inheritance through prototypes. When exploited, prototype pollution allows attackers to manipulate the properties of objects, which can lead to arbitrary code execution, denial of service (DoS), or complete application compromise.
In this tutorial, we will explore the mechanics of prototype pollution, how it can be exploited, and the steps you can take to defend your applications from these types of attacks. We’ll go through beginner, intermediate, and advanced examples to understand how attackers can leverage this vulnerability and how developers can avoid falling victim to it.
Table of Contents
- What is JavaScript Prototype Pollution?
- How Prototype Pollution Works
- The Impact of Prototype Pollution
- Real-World Examples of Prototype Pollution
- Defending Against Prototype Pollution
- Conclusion
What is JavaScript Prototype Pollution?
Prototype pollution is a vulnerability that occurs when an attacker can modify an object’s prototype, usually Object.prototype, in JavaScript. Since all objects in JavaScript inherit properties from the prototype, modifying the prototype directly affects all objects that inherit from it. This opens up a wide range of possibilities for an attacker, such as injecting malicious code or manipulating application logic.
JavaScript uses a prototype-based inheritance system, where objects can inherit properties and methods from other objects. Every object in JavaScript has an internal property called [[Prototype]], which points to its prototype. In simple terms, if an object doesn’t have a certain property, JavaScript looks up the chain through the prototype to find it.
Why is Prototype Pollution Dangerous?
When an attacker can modify the prototype of fundamental objects such as Object, they can insert malicious properties that can later be used in unexpected ways by other parts of the application. This can lead to severe vulnerabilities like code execution, data leakage, or bypassing security checks.
How Prototype Pollution Works
At its core, prototype pollution happens when user-controlled input is used to modify the prototype chain. This can happen in situations where applications allow object creation or property assignment without proper validation. Let’s take a look at a basic example of how this might occur.
Basic Example of Prototype Pollution
/**
* Demonstrates how prototype pollution works by adding a property to the prototype chain.
* @example
* let obj = {};
* obj.__proto__.polluted = 'Yes, I am polluted!';
* console.log({}.polluted); // Outputs: 'Yes, I am polluted!'
*/
let obj = {};
obj.__proto__.polluted = 'Yes, I am polluted!';
console.log({}.polluted); // Outputs: 'Yes, I am polluted!'
In this code, an attacker sets a property on the prototype of all objects by modifying __proto__. Now, every object created in this environment will inherit the polluted property.
Explaining the Prototype Chain
JavaScript objects inherit properties from their prototype. For example, if you create an empty object {}, it will still inherit methods like toString() from Object.prototype. When the prototype chain is manipulated, it affects not only the current object but also all other objects that share that prototype.
Example of Prototype Pollution in User Input
A common scenario where prototype pollution can occur is when an application parses JSON data and assigns it directly to an object without validation. Let’s see an example where user input can lead to prototype pollution.
/**
* Demonstrates prototype pollution through user input manipulation.
* @example
* let userInput = JSON.parse('{"__proto__": {"admin": true}}');
* Object.assign(obj, userInput);
* console.log({}.admin); // Outputs: true
*/
let userInput = JSON.parse('{"__proto__": {"admin": true}}');
let obj = {};
Object.assign(obj, userInput);
console.log(obj.admin); // Outputs: undefined
console.log({}.admin); // Outputs: true, as the prototype has been polluted
In this example, the user input contains the __proto__ key. By assigning this input to the object, we are indirectly modifying the prototype of all objects. This type of vulnerability can easily lead to logic errors and security issues within the application.
The Impact of Prototype Pollution
Prototype pollution can lead to a variety of security issues, ranging from application crashes to remote code execution. Here are some of the key impacts:
- Denial of Service (DoS): Attackers can cause the application to behave in unexpected ways, leading to crashes or resource exhaustion by modifying core properties like
toStringorvalueOf. - Arbitrary Code Execution: If the application relies on object properties for executing specific logic, attackers can modify the prototype to inject malicious code.
- Privilege Escalation: In scenarios where object properties are used to manage permissions (e.g.,
isAdmin), prototype pollution can allow attackers to gain unauthorized access.
Example: Manipulating Application Logic
An attacker could modify a security check within an application to bypass authentication. For instance, an application might check if a user is an admin by inspecting the isAdmin property. Through prototype pollution, an attacker could force all objects to inherit isAdmin: true, bypassing the security check.
/**
* Example of how an attacker can manipulate application logic to gain unauthorized access.
* @example
* user.__proto__.isAdmin = true;
*/
let user = { name: 'regularUser' };
// Security check
if (user.isAdmin) {
console.log('Access granted.');
} else {
console.log('Access denied.');
}
// Attacker manipulates the prototype
user.__proto__.isAdmin = true;
console.log(user.isAdmin); // Outputs: true
In this example, the attacker successfully modifies the prototype, gaining unauthorized access.
Real-World Examples of Prototype Pollution
Prototype pollution vulnerabilities have been found in several popular JavaScript libraries. These vulnerabilities are particularly dangerous because they allow attackers to manipulate entire application states through a single entry point.
1. Lodash Library Prototype Pollution
Lodash, a popular utility library for JavaScript, was found to be vulnerable to prototype pollution in certain versions. The merge() and defaults() functions, which are often used to merge objects, were not properly sanitizing input. Attackers could craft input objects with __proto__ properties and modify the prototype chain.
2. jQuery’s Vulnerability
jQuery, another widely used JavaScript library, has also been found vulnerable to prototype pollution attacks in the past. Attackers could use input fields or other user-controlled data to modify the prototype and affect the functionality of the application.
Defending Against Prototype Pollution
Defending against prototype pollution requires a combination of secure coding practices and awareness of how user input is handled. Here are some key strategies:
1. Avoid Using __proto__ Directly
Always avoid directly using __proto__ in your code, as it can lead to unexpected prototype manipulations. Instead, use functions like Object.create() to define object inheritance in a safer manner.
2. Sanitize and Validate User Input
Ensure that all user input is properly validated before being used in object assignments. Avoid assigning user-controlled data to the prototype chain or using unchecked data in object manipulation functions like merge() or assign().
3. Use Object.freeze() for Critical Objects
For objects that should not be modified, consider using Object.freeze(). This prevents changes to an object’s properties and protects against prototype pollution.
/**
* Use Object.freeze() to prevent critical objects from being modified.
* @example
* const secureObject = Object.freeze({ key: 'value' });
* secureObject.key = 'newValue'; // No effect
*/
const secureObject = Object.freeze({ key: 'value' });
// Attempt to modify the object
secureObject.key = 'newValue'; // This will not change the value
4. Regularly Update Dependencies
Many prototype pollution vulnerabilities are discovered in third-party libraries, so it’s essential to keep your dependencies up to date. Regularly audit your project for security issues and apply patches promptly.
5. Use Security Tools
Finally, use security scanning tools to automatically detect prototype pollution vulnerabilities in your code. Tools such as Snyk or npm audit can help identify and fix vulnerabilities in your application’s dependencies.
Conclusion
JavaScript prototype pollution is a serious vulnerability that can lead to widespread security issues, including arbitrary code execution, denial of service, and application compromise. Understanding how prototype pollution works and taking proactive steps to secure your applications can help protect against these attacks.
By validating user input, using secure coding practices, and keeping your dependencies up to date, you can significantly reduce the risk of prototype pollution in your JavaScript applications. Stay vigilant, and ensure that your development practices include proper security measures to protect against evolving threats.

