Prototype-polluting function¶
ID: js/prototype-pollution-utility
Kind: path-problem
Security severity: 6.1
Severity: warning
Precision: high
Tags:
- security
- external/cwe/cwe-078
- external/cwe/cwe-079
- external/cwe/cwe-094
- external/cwe/cwe-400
- external/cwe/cwe-471
- external/cwe/cwe-915
Query suites:
- javascript-code-scanning.qls
- javascript-security-extended.qls
- javascript-security-and-quality.qls
Click to see the query in the CodeQL repository
Most JavaScript objects inherit the properties of the built-in Object.prototype
object. Prototype pollution is a type of vulnerability in which an attacker is able to modify Object.prototype
. Since most objects inherit from the compromised Object.prototype
, the attacker can use this to tamper with the application logic, and often escalate to remote code execution or cross-site scripting.
One way to cause prototype pollution is through use of an unsafe merge or extend function to recursively copy properties from one object to another, or through the use of a deep assignment function to assign to an unverified chain of property names. Such a function has the potential to modify any object reachable from the destination object, and the built-in Object.prototype
is usually reachable through the special properties __proto__
and constructor.prototype
.
Recommendation¶
The most effective place to guard against this is in the function that performs the recursive copy or deep assignment.
Only merge or assign a property recursively when it is an own property of the destination object. Alternatively, block the property names __proto__
and constructor
from being merged or assigned to.
Example¶
This function recursively copies properties from src
to dst
:
function merge(dst, src) {
for (let key in src) {
if (!src.hasOwnProperty(key)) continue;
if (isObject(dst[key])) {
merge(dst[key], src[key]);
} else {
dst[key] = src[key];
}
}
}
However, if src
is the object {"__proto__": {"isAdmin": true}}
, it will inject the property isAdmin: true
in Object.prototype
.
The issue can be fixed by ensuring that only own properties of the destination object are merged recursively:
function merge(dst, src) {
for (let key in src) {
if (!src.hasOwnProperty(key)) continue;
if (dst.hasOwnProperty(key) && isObject(dst[key])) {
merge(dst[key], src[key]);
} else {
dst[key] = src[key];
}
}
}
Alternatively, block the __proto__
and constructor
properties:
function merge(dst, src) {
for (let key in src) {
if (!src.hasOwnProperty(key)) continue;
if (key === "__proto__" || key === "constructor") continue;
if (isObject(dst[key])) {
merge(dst[key], src[key]);
} else {
dst[key] = src[key];
}
}
}
References¶
Prototype pollution attacks: lodash, jQuery, extend, just-extend, merge.recursive.
Common Weakness Enumeration: CWE-78.
Common Weakness Enumeration: CWE-79.
Common Weakness Enumeration: CWE-94.
Common Weakness Enumeration: CWE-400.
Common Weakness Enumeration: CWE-471.
Common Weakness Enumeration: CWE-915.