Protecting Your Data: Preventing Prototype Pollution Attacks

how to prevent prototype pollution

Prototype pollution is a critical vulnerability that allows attackers to manipulate a JavaScript application's objects and properties, leading to serious security issues. This vulnerability was introduced when the European Association of Computer Manufacturers (ECMA) published the ECMAScript2015, 6th Edition specification, creating a JavaScript standard that ensures compatibility across various web browsers. Prototype pollution attacks occur when a hacker sends a malicious payload to a backend server, and an unsafe merge function recursively merges that payload with a backend object. This results in the attacker manipulating the __proto__ attribute, usually by adding a new prototype into it. To prevent prototype pollution, developers can use new Set() or new Map() instead of object literals, and create objects with a null prototype to ensure they don't inherit any properties. Additionally, the Object.freeze() and Object.seal() methods can be used to prevent prototype objects from being changed, providing built-in protection.

shunwaste

Use a safe merge function to prevent malicious input from affecting the prototype

Prototype pollution is an injection attack that targets JavaScript runtimes. This attack allows a hacker to send a malicious payload to the backend server, and if an unsafe merge function is used, the payload gets merged with a backend object. This can allow attackers to manipulate an application's JavaScript objects and properties, leading to serious security issues such as unauthorized access to data, privilege escalation, and even remote code execution.

A safe merge function can prevent malicious input from affecting the prototype and mitigate prototype pollution attacks. When implementing a recursive merge function, developers must ensure that a key with the value __proto__ is not copied from one object to another. This can be achieved by sanitizing property keys before merging them into existing objects, using an allowlist of permitted keys.

Another way to prevent prototype pollution is to use objects that provide built-in protection, such as Map or Set. These built-in methods only return properties that were defined directly on the object itself, helping to eliminate gadgets that attackers can exploit.

Additionally, developers can use the Object.freeze() method to ensure that an object's properties and values cannot be modified, and no new properties can be added. Similarly, the Object.seal() method can be used to prevent changes to the object while still allowing modifications to the values of existing properties.

It is also important to note that many popular vulnerable libraries, such as jQuery, lodash, and hoek, have been updated to address prototype pollution. Keeping your libraries up to date and regularly scanning for vulnerabilities can help prevent prototype pollution attacks.

shunwaste

Prevent prototype objects from being changed by using Object.freeze() and Object.seal()

Preventing changes to prototype objects is a robust approach to preventing prototype pollution vulnerabilities. This can be achieved by invoking the Object.freeze() and Object.seal() methods.

Object.freeze() is a static method that freezes an object, preventing extensions and making existing properties non-writable and non-configurable. In other words, a frozen object is read-only and cannot be changed in any way. New properties cannot be added, existing properties cannot be removed, and their enumerability, configurability, writability, or values cannot be changed. This is the highest integrity level provided by JavaScript.

For example:

Javascript

Const obj = {

Prop: 42,

};

Object.freeze(obj);

Obj.prop = 33; // This will throw an error

Object.seal() is also a static method, but it only seals an object, preventing extensions and making existing properties non-configurable. However, unlike Object.freeze(), the values of existing properties in a sealed object can still be changed as long as they are writable. This means that while new properties cannot be added, and existing properties cannot be removed or converted, their values can be modified.

Javascript

Const obj = {

Foo: "bar",

};

Const sealedObj = Object.seal(obj);

SealedObj.foo = "baz"; // This is allowed

Delete sealedObj.foo; // This is not allowed

Both Object.freeze() and Object.seal() can be used to prevent built-in prototypes from being modified, which is an effective defence against prototype pollution. However, it is important to note that using these methods on objects used by libraries can break the application if those libraries modify the built-in prototypes.

shunwaste

Use an allowlist of permitted keys to sanitize property keys before merging

Preventing prototype pollution vulnerabilities requires sanitizing property keys before merging them into existing objects. This prevents attackers from injecting keys that reference an object's prototype. Using an allowlist of permitted keys is an effective way to sanitize property keys.

An allowlist approach ensures that only approved keys are allowed, blocking any unauthorized or malicious inputs. By defining a set of permitted keys, you can validate each key before merging it with an existing object. This way, any attempt to inject unauthorized keys, such as "__proto__", will be rejected.

Implementing an allowlist provides a robust defense mechanism. It ensures that only expected and safe keys are merged, mitigating the risk of prototype pollution. This proactive approach prevents potential security breaches and unauthorized access to data.

Additionally, the allowlist can be regularly reviewed and updated. By maintaining a controlled list of permitted keys, you can adapt to evolving security threats and address any newly discovered vulnerabilities. This allows for a dynamic security measure that can be fine-tuned over time.

Allowlists can be particularly useful when combined with other security measures, such as input validation and output escaping. By employing multiple layers of defense, you can create a more resilient system that effectively prevents prototype pollution and safeguards your applications.

shunwaste

Use built-in protection objects like Map and Set

Using built-in protection objects like Map and Set can help prevent prototype pollution attacks. Prototype pollution is an injection attack that targets JavaScript runtimes, where an attacker manipulates an application's JavaScript objects and properties, leading to serious security issues such as unauthorized data access, privilege escalation, and remote code execution.

By default, all objects inherit from the global Object.prototype, either directly or indirectly via the prototype chain. However, you can manually set an object's prototype by creating it using the Object.create() method, which lets you assign any object as the new prototype. This can be useful to prevent prototype pollution as you can create an object with a null prototype, ensuring it doesn't inherit any properties.

Maps and Sets provide built-in methods that only return properties defined directly on the object itself. For example, when defining an options object, you can use a Map, which has a built-in get() method that only returns properties defined directly on the map. A Set works similarly, providing built-in methods that only return properties defined directly on the object.

While Maps and Sets don't eliminate all risks, they significantly reduce them. They can help prevent attackers from injecting keys that reference an object's prototype, such as __proto__. However, it's important to note that attackers can still overwrite and change the behaviour of these built-in methods, so additional security measures may be necessary.

shunwaste

Upgrade package.json dependencies to the latest versions

Prototype pollution is a critical vulnerability that allows attackers to manipulate an application's JavaScript objects and properties, leading to serious security issues. It is an injection attack that targets JavaScript runtimes. Developers should use new Set() or new Map() instead of using object literals. Objects should be created using the Object.create(null) API to ensure they don't inherit from the Object prototype.

To upgrade package.json dependencies to the latest versions, you can use npm, the package manager for Node.js. Here are the steps:

  • Open a command prompt or terminal window in the root directory of your project.
  • Run the command "npm install" to install all the dependencies listed in your package.json file.
  • To update a specific dependency, use the command "npm update package-name", replacing "package-name" with the name of the dependency you want to update.
  • To update all the dependencies in your package.json file to their latest versions, use the command "npm update".
  • After updating, verify that your project still works as expected and fix any issues if necessary.

It is important to regularly update your dependencies to receive security patches, bug fixes, new features, and maintain compatibility with other tools. However, before upgrading, it is recommended to verify that there are no breaking changes in your dependencies. You can use npm outdated to check for outdated dependencies and npm-upgrade to see the current version and choose whether to upgrade.

Frequently asked questions

Prototype pollution is a security vulnerability that occurs when an attacker is able to manipulate the prototype of JavaScript objects. This can lead to unexpected behaviour in your application, potentially resulting in denial of service, remote code execution, or even cross-site scripting attacks.

In JavaScript, all objects inherit properties from their prototypes. Prototype pollution occurs when an attacker manipulates the __proto__ attribute, usually by adding a new prototype to it. This can be done by injecting properties into existing JavaScript code, which can trigger a JavaScript exception causing denial of service, remote code execution, or other severe consequences.

There are several ways to prevent prototype pollution vulnerabilities:

- Use the Object.freeze() and Object.seal() methods to prevent prototype objects from being changed.

- Use objects that provide built-in protection, such as Map or Set.

- Sanitize property keys before merging them into existing objects, using an allowlist of permitted keys.

- Upgrade package.json dependencies to the latest versions.

Some tools that can help mitigate prototype pollution attacks include:

- Snyk Advisor: Provides information on package popularity, community support, and security.

- Snyk: A vulnerability scanner that notifies you about new vulnerabilities in your libraries.

- Imperva Runtime Application Self-Protection (RASP): Real-time attack detection and prevention for your application runtime environment.

- Imperva Web Application Firewall (WAF): World-class analysis of web traffic to prevent attacks.

Written by
Reviewed by

Explore related products

Share this post
Print
Did this article help you?

Leave a comment