Jan 18 meeting notes

David Bruant bruant.d at gmail.com
Thu Jan 19 01:43:16 PST 2012


Le 19/01/2012 06:44, Brendan Eich a écrit :
>> Use __proto__ in object literals to do a put (assuming that a 
>> __proto__ getter/setter was created in Object.prototype) instead of a 
>> defineProperty?  All modes or only nonstrict mode?
>> Allen: Make such use of __proto__ to be a synonym for <|.  If a <| is 
>> already present, it's an error.
>> DaveH: __proto__ is ugly.  Don't want it in the language forever.
>> Waldemar: What about indirect [] expressions that evaluate to 
>> "__proto__"?  In Firefox they evaluate to accesses that climb the 
>> prototype chain and usually reach a magic 
>> getter/setter-that-isn't-a-getter-setter named __proto__ that sits on 
>> Object.prototype.
>> MarkM: Likes the ability to delete __proto__ setter and thereby 
>> prevent anything in the frame from mutating prototypes.
>> Waldemar: How do you guard against cross-frame prototype mutations?
>> DaveH: __proto__ is in the "omg, what were we thinking" category.
>> Waldemar: Opposed to making __proto__ mutate prototypes other than at 
>> object construction.  This is getting insanely complex.
>> Unresolved.
>
> One point not recorded here: given MarkM's argument for 
> Object.prototype.__proto__ as the one property to delete to remove 
> this old beast, what kind of property does that appear to be to ES5's 
> Object.getOwnPropertyDescriptor? Arguments pro and con for data 
> property (as it appears to be in SpiderMonkey) vs. accessor (JSC 
> intended to move to that from its hardcoded magic id handling in Get 
> and Put code).
>
> Argument for data property facade: an accessor allows extracting the 
> setter from the property descriptor, call it stolen__proto__setter. 
> Then if one makes an object with a bespoke proto-object but not 
> delegating to Object.prototype:
>
>   var o = { __proto__: Object.create(null) };
>
> an attacker could mutate o's [[Prototype]] via 
> stolen__proto__setter.call(o, evil_proto). This is not possible if 
> Object.prototype.__proto__ reflects as a data property, because o's 
> two-level proto chain is cut off from Object.prototype, so no further 
> means of updating [[Prototype]] is available.
Every time I've been thinking of an issue like this, the solution I've 
found was "whoever runs first wins".
Assuming __proto__ is an accessor of Object.prototype:
If trusted code runs first, it can protect itself by removing the setter 
and making the property non-configurable.
If an attacker runs first... you're screwed as you made the demonstration.

Even in the data property case, if an attacker runs first, she can 
probably change quite a lot of built-in prototypes, change built-in 
properties (of any object it has access to) to non-configurable 
accessors, add loggers, all over the place, return evil values to 
function calls.
I have a script [1] which replaces every function with a function that 
is semantically equivalent, but logs "this", the arguments and the 
return value. If an attacker runs this before any other script (before 
initSES.js, for instance :-° ), but adds more harmful than loggers, she 
can really do nasty stuffs.

It seems that the threat may be a bit smaller if __proto__ is a data 
property, but I'm not sure it's significantly smaller than all the 
things you can already do if an attacker runs first.

If you run first, __proto__ being an accessor or a data property does 
not make a difference, you can protect yourself in any case.
The accessor has the advantage that you can have fine-grained control 
over who can change and what cannot. Specifically, you can bind 
__proto__setter, and share this with someone so that this party can 
change the prototype of a given object (or set of objects) you've 
chosen. A data property is more "all (everyone can change the prototype 
of every object inheriting from Object.prototype) or nothing (or no one 
can change the prototype of any object)".


The question that remains is "how can you make sure your trusted run 
first?" which, I think goes beyond ECMAScript scope and should be 
considered in each context (browser, node.js, etc.)

For the browser, I can't think of a good solution that would be backward 
compatible and efficient. Suggestions welcome.

David

[1] https://github.com/DavidBruant/JSTraversers (not really production 
ready and makes some browser crash or hang, because they don't seem to 
appreciate their DOM builtins being traversed)


More information about the es-discuss mailing list