Skip to content

Prevent Prototype Manipulation

The Node.js runtime has introduced a feature to prevent a security vulnerability specific to JavaScript known as prototype pollution. This feature, enabled using a command-line flag, helps to prevent modifications and manipulation to the prototype of objects that are known as Primitives. These primitives are the building blocks of JavaScript, and they include Object, Array, Function, and String.

The two Node.js command-line flags that can be enabled to remove prototype pollution related vulnerabilities are:

  • --disable-proto=<mode> where mode can be one of throw or warn.
  • --frozen-intrinsics which freezes the built-in objects to prevent modifications.

Simple Example of Prototype Poisoning

Consider the following example to demonstrate a prototype poisoning vulnerability:

const payload = '{"__proto__": {"isAdmin": true}}';
const obj = {};
const parsedPayload = JSON.parse(payload);
Object.assign(obj, parsedPayload);
// this will print true
console.log(obj.isAdmin);

In the first phase, we demonstrate how a payload received from users may be a JavaScript object. That object, may be manipulated to include a __proto__ property, which is then assigned to an empty object. In the second phase, when JSON.parse() is called, the __proto__ property is treated as a valid property and together with the Object.assign() method, this combination results in the isAdmin property being added to the empty object.

The end result is that the isAdmin property gets added to the empty object, which is then printed to the console. This overrides the __proto__ property of the object, which is a security vulnerability.

Simple Example of Prototype Pollution

Often, prototype pollution is demonstrated with the classic example of unsafe deep merging of objects. So popular, that this practice has been ill-followed by developers and maintainers so much that dozens of libraries, including lodash, have been affected by this vulnerability.

For the purpose of our demonstration though, I will display a simple example of prototype pollution that uses direct object access to pollute the Object prototype. We will do so by specifically accessing 2-dimensional nested properties of an object.

const obj =
{
user: {
name: 'John',
age: 30,
addresses: {
home: {
street: '123 Main St',
city: 'Springfield',
state: 'IL',
zip: '62701'
},
work: {
street: '456 Elm St',
city: 'Springfield',
state: 'IL',
zip: '62701'
}
}
}
};
// insecure access to direct object properties
obj.user['__proto__']['isAdmin'] = true;
// this will print true
console.log(obj.isAdmin);
let newObj = {};
console.log(newObj.isAdmin);

Prevent Prototype Pollution

To minimize the impact of prototype pollution for Node.js applications, you should enable the Node.js runtime flag --disable-proto=throw and --disable-proto=warn. This will throw an error or log a warning when a prototype pollution attempt is detected.

Terminal window
$ node --disable-proto=throw --frozen-intrinsics server.js