Search code examples
javascriptsecurityprototypeexploit

Is `Object.fromEntries()` secure from prototype pollution?


Consider the following simple example of prototype pollution in JavaScript:

function sayHello(name) {
  console.log(`Hi ${name}!`);
}

// Pollute the prototype
({}).__proto__.toString = () => alert('hacked');

// Trigger the exploit
sayHello({});

I was wondering if a similar exploit could be done with Object.fromEntries, so I tested:

function sayHello(name) {
  console.log(`Hi ${name}!`);
}

// Try to pollute the prototype, but doesn't work, even for the same object!
const x = Object.fromEntries([['__proto__', { toString: () => alert('hacked') }]]);

// Try to trigger the exploit, but fail
sayHello({}); // Hi [object Object]
sayHello(x); // Hi [object Object]

The fact that the built-in Object.fromEntries is safe from this exploit is great, I was expecting some protection. However, I thought it would either throw an error or skip setting the __proto__, but to my surprise the __proto__ was actually set!

x.__proto__.toString(); // Exploited!
x.toString(); // Not exploited!!

I was very surprised that Object.fromEntries managed to create an object whose .__proto__.toString is exploited while .toString is not.

So, is this safe?

Can I use Object.fromEntries with unchecked user-supplied data safely?


Solution

  • Can I use Object.fromEntries with unchecked user-supplied data safely?

    Yes, it will never modify Object.prototype by building an object.

    I was very surprised that Object.fromEntries managed to create an object whose .__proto__.toString is exploited while .toString is not.

    There's nothing special about .__proto__ here, it's just a getter/setter property on Object.prototype, similar to hasOwnProperty or isPrototypeOf.

    You will notice that Object.fromEntries does build an object with an own .__proto__ property, and that x.__proto__ !== Object.prototype (although still Object.getPrototypeOf(x) === Object.prototype). The inherited property is shadowed.