JavaScript prototype pollution attack in NodeJS

Prototype pollution attack in NodeJS application

General concept of the attack

The general idea behind prototype pollution starts with the fact the attacker has control over at least the parameter a and value of any expression of the following form:

obj[a][b] = value;

The attacker can set a to __proto__ and the property with the name defined by b will be defined on all existing object (of the class of obj) of the application with the value value.

The same thing can append with the following form when the attacker has at least control of a, b and value.

obj[a][b][c] = value;

The attacker can set a to constructor, b to prototype and the property with the name defined by c will be defined on all existing object of the application with the value value.

However since this requires more complex object assignment, the first form is easier to work with.

While, it’s pretty rare that you will stumble on code that looks textually like the example provided, some manipulation can provide the attacker with similar control.

Mitigation

Using Map instead of Object

It essentially works as a HashMap, but without all the security caveats that Object have. When a key/value structure is needed, Map should be preferred to Object.

Object.create(null)

It’s possible to create object in JavaScript that don’t have any prototype. It requires the usage of the Object.create function. Object created through this API won’t have the __proto__ and constructor attributes. Creating object in this fashion can help mitigate prototype pollution attack.

let obj = Object.create(null);
obj.__proto__ // undefined
obj.constructor // undefined

Schema validation of JSON input

Multiple library on npm (ex.: ajv ) offer schema validation for JSON data. Schema validation ensure that the JSON data contains all the expected attributes with the appropriate type. When using this approach to mitigate “prototype pollution” attack, it’s important that unneeded attributes are rejected. In ajv, this can be done by setting additionalProperties to false on the schema.

Freezing the prototype

Using Object.freeze will mitigate almost all the exploitable case.

Note that while, adding function to the prototype of the base object is a frown upon practice, it may still be used in your Node.js application or its dependency. It’s highly recommend checking your Node.js application and its dependency for such usage before going down this route. Since the behavior of frozen object is to silently fail on property assignation, it may introduce hard to identify bug.

Object.freeze(Object.prototype);
Object.freeze(Object);
({}).__proto__.test = 123;
({}).test // this will be undefined

Source