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?
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.