Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active December 15, 2015 15:29
Show Gist options
  • Save WebReflection/5282385 to your computer and use it in GitHub Desktop.
Save WebReflection/5282385 to your computer and use it in GitHub Desktop.
`__proto__` is dangerous

Object.setPrototypeOf() polyfills

Non greedy in 95 bytes (function(O,P){P in O||(O[P]=function(o,p){o.__proto__=p;return o})})(Object,'setPrototypeOf');

Or via self reassignment in 91 bytes (function(O,P){O[P]=O[P]||function(o,p){o.__proto__=p;return o}})(Object,"setPrototypeOf");

Or direct call, 65 bytes, for you closure! var p=Object.setPrototypeOf||function(o,p){o.__proto__=p;return o};

Why This And What Is It

I am bored by explaing with words, it's time to show some example. All of this is because developers are both lazy and stubborns and prefer a dirty and risky way instead of embrace and propose the usage of an explicit Object.setPrototypeOf(obj, source):obj which is truly easy to polyfill:

var proto = Object.setPrototypeOf || function (o, p) {
  // for jusrassic browsers still adopting this mess
  o.__proto__ = p;
  return o;
};

// instead of 
{__proto__: proto}

proto({}, proto);
// even smaller

Not only performance is preserved, above example is conveniente against obj[key] = value too because it's an explicit operation with an explicit developer intent.

It is not possible that by accident or by malicious code your objects could suddenly change inherithance.

How? Here different examples


Generic And Classic Method

The very first example is about setting a generic object property.

var o = {
  setValue: function (key, value) {
    this[key] = value;
  }
};

// third parts, user input
// whatever external or internal
// non sanitized source
var genericKey = '__proto__';

// from now on, every object inheriting o
// and the `o` object included are affected
o.setValue(genericKey, null);

// bye bye o and all instances
alert(o instanceof Object); // false

Bear in mind, o could be any Constructor.prototype object.

RealWorld: A Generic Emitter Class

Most emitters are based into direct property set into objects rather than empty dictionaries whcih means these are all unsafe. node.js as example uses an object as this._events and the same is on the web.

Specially on latter one, a malicious code could change that prototype injecting as proottype a Proxy or an object fulls of setters and getters and intercept basically all events and data fired through the library in a completely transparent way.

To protect your code against this kind of attack use this._events = Object.create(null); instead so there's no way any third parts or malicious code could hook into your events. This is also better if you want to use any sort of name for your events, included those present in the Object.protoype if needed.

Otherwise you can check that every time your library adopted by third parts does not set a key string which content is __proto__

Underscore.js

Using _.extend(a, b) with this library might result in different behavior. Mobile and desktop Chrome browsers will be safe with next example while all new desktop Browsers following ES6 will break the source obj object.

var dict = Object.create(null),
    obj = {generic: 'object'};

// a wild key with a wild special property
var key = '__proto__';

// malicious code or any sor tof code
// that uses a dictionary the way it's meant to be used
dict[key] = Array.prototype;

// now a call to underscore.js
_.extend(obj, dict);

// the object is not the object we know anymore
alert(obj instanceof Array); // true

Bear in mind __proto__ is just a key as any other key and dictionaries are not aware of any special key indeed. However, the bad part is that any object could become a Dictionary following the very first example.

The problem in latter example is that method won't be consitent but the library goal is to make these kind of operations consistents and as safe as possible cross platform.

The reason developers used __proto__ is because it was not enumerable but this was reflected into Object.create(null) too while now, accordingly with specs, dictionaries will have that as enumerable property (which is correct and expected behavior)

Why Would I ... (bla bla bla)

Remember ES5 introduced "use strict"; directive to avoid developers leaking by accident global variables because of undefined or null this context while calling a generic function? The issue here is exactly the same, the possibility, by accident, to change an entire application behavior (security) and the fact your library in 3rd parts hands is not secure anymore (included exposed APIs)

The Solution Is Easy: proto Should Disappear!

Again use Object.setPrototypeOf() is not slower, is not problematic in term of size, is just a hint for TC39 to see developers don't want such magic property in the middle.

This property is much worst than any non enumerable bug in old IEs ... you don't want this mess in the specs, do you?

Thanks for your listening and understanding.

More Resources

Well, my battle is all over but will end up hopefully here 'cause I don't know anymore how to explain this issue for both security and consistency with ECMAScript.

Happy easter

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