Prototype Pollution
detect, understand, remediate
Prototype pollution lets attackers inject properties into JavaScript Object prototypes through unsafe merge or clone operations, potentially leading to XSS, RCE, or denial of service.
No credit card required. Free plan available forever.
What is prototype pollution?
Prototype pollution is a JavaScript-specific vulnerability, classified under CWE-1321, that allows an attacker to inject properties into JavaScript object prototypes. Since nearly every object in JavaScript inherits from Object.prototype, polluting it means that the injected properties become available on all objects throughout the application. This can lead to denial of service, property injection, and in severe cases, cross-site scripting or remote code execution.
The vulnerability arises when applications use recursive merge, deep clone, or property assignment functions that do not filter special keys like __proto__, constructor, and prototype. Attackers exploit these by submitting crafted JSON payloads through API requests, query parameters, or form data. Libraries like older versions of lodash, jQuery extend, and Hoek have historically been vulnerable, making this issue closely related to vulnerable dependency management.
Prototype pollution is particularly insidious because the polluted property does not affect the source object alone. It propagates globally, meaning that a single malicious request can alter the behavior of every subsequent operation in the application process. This makes exploitation difficult to trace and can cause cascading failures. Modern JavaScript applications, both client-side and server-side (Node.js), must treat prototype pollution as a high-priority vulnerability that demands both code-level analysis and dependency auditing to detect.
How it works
Identify merge or extend function
The attacker locates an endpoint that processes JSON input through a recursive merge, deep clone, or property assignment operation that does not filter prototype keys.
Inject __proto__ payload
A crafted JSON payload is submitted containing a __proto__ key with malicious property values, such as {"__proto__": {"isAdmin": true}} or {"constructor": {"prototype": {"polluted": true}}}.
Pollute Object prototype
The merge function processes the payload and assigns the attacker-controlled properties to Object.prototype, making them accessible on every object in the application.
Trigger secondary vulnerability
The polluted property is consumed by application logic elsewhere, leading to XSS (through polluted template variables), RCE (through polluted shell command options), or DoS (through corrupted application state).
Common causes
Recursive merge without key filtering
Custom or library-provided deep merge functions that iterate over all keys including __proto__ and constructor.prototype without checking whether they reference the object prototype chain.
Vulnerable library versions
Older versions of widely-used libraries like lodash (before 4.17.12), jQuery ($.extend in deep mode), and minimist contain prototype pollution vulnerabilities that are well-documented and easily exploited.
JSON.parse with unchecked keys
Parsing user-supplied JSON and passing the result directly to merge or assign operations without validating or sanitizing the key names. JSON.parse preserves __proto__ as a regular property key.
Object.assign and spread misuse
Using Object.assign or spread operators to merge user-controlled objects with application configuration or default settings. While shallow spread is safer, nested patterns combined with other operations can still enable pollution.
How to detect it
Automated detection
- SecPortal's code scanner uses Semgrep rules to detect unsafe merge patterns, recursive property assignment without key filtering, and direct use of known vulnerable library functions
- SCA (software composition analysis) scanning identifies dependencies with known prototype pollution CVEs, including vulnerable versions of lodash, minimist, Hoek, and set-value
- SAST analysis traces data flow from user input (request body, query parameters) through merge or assign operations to identify exploitable prototype pollution sinks
Manual testing
- Submit JSON payloads containing __proto__ keys to API endpoints and check whether the injected properties appear on unrelated objects in subsequent responses
- Review package.json and lock files for dependencies with known prototype pollution advisories using npm audit or Snyk
- Audit custom merge, clone, and extend utility functions in the codebase for missing __proto__ and constructor key filtering
How to fix it
Use Object.create(null) for lookup objects
Create objects without a prototype chain by using Object.create(null). These objects do not inherit from Object.prototype, so polluting the prototype has no effect on them. Use this pattern for configuration objects, caches, and maps.
Freeze prototypes
Call Object.freeze(Object.prototype) early in your application startup to prevent any modifications to the base prototype. This is a defense-in-depth measure that blocks pollution attempts at runtime.
Validate and sanitize object keys
Before merging user-supplied objects, filter out dangerous keys including __proto__, constructor, and prototype. Implement an allow-list of expected keys rather than a deny-list to ensure comprehensive protection.
Update vulnerable libraries
Keep dependencies up to date. Replace vulnerable versions of lodash, jQuery, minimist, and similar libraries with patched versions. Run regular dependency audits as part of your CI/CD pipeline to catch new advisories.
Use Map instead of plain objects
For dynamic key-value storage where keys come from user input, use JavaScript Map objects instead of plain objects. Maps do not have a prototype chain that can be polluted and provide a cleaner API for dynamic data.
Compliance impact
Find prototype pollution in your code
SecPortal's code scanner detects unsafe merge, extend, and deep clone patterns that enable prototype pollution. Start free.
No credit card required. Free plan available forever.