How to clean up __proto__ (was: Why we need to clean up __proto__)

David Bruant bruant.d at gmail.com
Thu Dec 29 17:11:12 PST 2011


Le 30/12/2011 01:00, Lasse Reichstein a écrit :
> On Thu, Dec 29, 2011 at 8:41 PM, Mark S. Miller <erights at google.com> wrote:
>> Hi Allen, that's very clever. But I don't think it is needed.
>>
>> David is right about running first. I also don't have a proof, but
>> practically I'm sure that for JS as it is and will continue to be under
>> ES-next, unless some trusted code runs in a context (frame) to initialize
>> itself before any untrusted code runs in that context, all is lost anyway.
> Pretty certainly.
> Malicious code can save and poison all of the methods on Object and
> Object.prototype, so it shouldn't be hard to ensure that
> Object.prototype.__proto__ is never made unconfigurable, so you can
> always put the setter back if you need it and remove it again
> afterwards.
> The only real question is whether it's detectable. I'm not convinced
> either way (there's a lot of subtle tricks and subtle counter-tricks
> possible, and I've been writing this paragraph for a while and kept
> coming up without counters to my previous examples in both directions
> :).
I think that once again, it all boils down to whether you run first.
If you run first, you can save a reference to anything in the
environment (getter/setters included). If you have a doubt on a
reference, you can check equality with yours.

If you do not run first, the attacker can make the environement look
like a normal one. Specifically, you can try to do
Object.defineProperty(Object.prototype, '__proto__',
{configurable:false}) and the attacker can later pretend that the
property is not configurable (in response to an
Object.getOwnPropertyDescriptor) even though it actually still is (and
she can still change the value at convenience).

When asking the question "can I detect that I am running first?", I
would answer that it's not always possible. If an attacker changes the
JavaScript environment in a way that is not observable to you
(functionally speaking), then, you cannot know that you have been
running second.

To take a specific example, regarding cookie theft, if you run first in
an ES5 + WebIDL compliant environment (regarding WebIDL, I think IE9 is
the closest implementation and the rest is far from it), you can take
the getter/setter pair of the cookie property somewhere in the prototype
chain of the document object. Then, you can delete the property so that
no code can neither access nor modify the cookie unless you hand them
either the getter or the setter.
On the other hand, if an attacker runs first, she can replace the
getter/setter pair with hers (and steal your cookie, by the way). When
your code runs (second), it cannot have a clue that someone ran first,
because the environment looks normal. When you use your cookie setter
function (which is the attacker function, but you can't know that), the
attacker code gets alerted and steals this new cookie as well. Your code
cannot know this happened, since it cannot know how the environment was
before it ran.

So, ES5+WebIDL allow to prevent cookie theft... assuming you run first.
If you don't, an attacker can steal your cookies anyway discretely
enough so that you don't know it.

On the good side of not being able to know that the environment changed
is that we can implement polyfill libraries. When code runs, it doesn't
know nor need to care whether features it uses are built-in or comes
from a library as long as they respect the expected specification.


I've been thinking about this "run first" idea for some time. Since on a
webpage, security seems to depend on your ability to run code first, it
would be interesting if there was a way to ensure that some code
(preferably defensive) is run before *any* other code. Though I find
this interesting, I'm still not sure whether this would be a good or bad
idea. I'm also clueless on how it would look like.
Creative ideas welcome.

David


More information about the es-discuss mailing list