Skip to content

Feature proposal: Mitigation for Client-Side Prototype Pollution #33

Description

@shhnjk

Client-Side Prototype Pollution (will refer as PP) are increasing. For example, this shows many libraries are vulnerable to PP just by parsing location.search.

But essentially, a PP bug can be introduced if a JS application has a custom function to merge/clone objects, where an object is controlled by an attacker.

For example, following is a vulnerable code:

function isObject(obj) {
  return obj !== null && typeof obj === 'object';
}

function merge(a, b) {
  for (let key in b) {
    if (isObject(a[key]) && isObject(b[key])) {
      merge(a[key], b[key]);
    } else {
      a[key] = b[key];
    }
  }
  return a;
}

const internalObj = {a: 1, b: 2};
const userSuppliedObj = JSON.parse('{"__proto__":{"polluted": 1}}');
merge(internalObj, userSuppliedObj);
const newObj = {};
console.log(newObj.polluted); // 1

Proposal

Create a Document Policy to stop PP.

Name: restrict-prototype (bikeshed).
Keywords: globalObject, allObjects, and (if possible) objectsIfImplicit (all bikeshed).

Note: I'm still unsure if Arrays should be protected. But if so, we can add same keywords for Arrays.

globalObject

This allows developers to set prototype of local objects, but not for the global Object.

For example:

let obj = {};
obj.__proto__.test = 1;
obj.test; // 1
Object.test; // undefined

This way, prototype addition/modification only affects a local object but not all other objects.

allObjects

This ignores prototype modification of all objects. Which is equivalent of Object.freeze(Object.prototype);

For example:

let obj = {};
obj.__proto__.test = 1;
obj.test; // undefined
Object.test; // undefined

objectsIfImplicit

This allows prototype modification only when explicitly specified.
Explicit prototype modification is defined by property set with the dot notation property accessor. Setting prototype other than the dot notation will be ignored (such as bracket notation).

For example:

let obj = {};
obj["__proto__"]["test"] = 1;
obj.test; // undefined
Object.test; // undefined

obj.__proto__.test = 1;
obj.test; // 1
Object.test; // 1

I'm not sure how technically feasible this option is (in terms of browser implementation), but it seems possible at least in Chromium.

This option is beneficial because most (if not all) of PP bugs are introduced with the code which accesses prototype with bracket notation (obviously this is unintentional). This option looks like a best way to eliminate PP while allowing developers to intentionally modify object prototypes.

Special thanks to @koto for some ideas.
CC: @mikewest and @arturjanc

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions