How to ensure that your script runs first in a webpage

David Bruant bruant.d at gmail.com
Sat Feb 4 03:29:42 PST 2012


Le 04/02/2012 01:14, John J Barton a écrit :
> On Fri, Feb 3, 2012 at 3:14 PM, David Bruant <bruant.d at gmail.com> wrote:
>> Le 03/02/2012 23:26, John J Barton a écrit :
>>> On Fri, Feb 3, 2012 at 1:08 PM, David Bruant <bruant.d at gmail.com> wrote:
>> I've been looking at Caja's code recently [1] and noticed that they take
>> (untrusted) code and eval it. Of course, doing a dummy eval would be
>> harmful because eval evaluates with the lexical environment of the
>> calling context. Instead, they've found a way to wrap the eval call and
>> make that when the eval code accesses something, they get to decide what
>> this value is. In a way, they provide the lexical environment of their
>> choice.
> And I'm guessing, but the objects in that environment need to be
> special in two ways: the need to have limited API and they need the
> restrictions like freeze().
It depends on what you call "limited". If you mean to limit the number
of functions/methods available, then no, you don't need to do this.
If you mean to limit the authority of existing API, then yes and that's
the best part of it: a trusted code can decide which authority (part of
a document, localStorage, XHR, etc.) is available and which is not.
Say you want to provide to a given script the ability to send XHRs to a
chosen URL, you can reimplement the API to only allow that and fake a
network error for any other URL.

> This would true of all their properties as
> well, including Object, Function etc.
It doesn't have to. It really depends on how much you trust the partialy
untrusted code. If you don't care of this code being able to mess with
extensible Array.prototype, you can do it (you probably rarerly do, though).
Also, forwarding proxies can be useful as they can forward operations
that you want and make fail other operations, so you can provide to
untrusted code something that looks like a fresh built-in Object, but
isn't and if the untrusted code freeze its version of Object.prototype,
it doesn't affect your version.

> The result (which is not unreasonable) is that the API objects have to
> be low level things, not say dojo widgets or so. Because otherwise the
> restrictions propagate through out the higher level code.
It doesn't have to. The advantage of providing attenuated
implementations of existing and standard APIs is that you can easily
leverage all code that rely on these APIs.
But if you ask untrusted parties to write code according to another API
(dojo widget if that's the kind of API you like), then you can do it.


>> A second argument to eval to provide a lexical environment could make
>> things easier. Just to be clear, I am not suggesting a second argument
>> like it was before [2]. The object properties would be the lexical
>> environment of the eval code (and not the lexical environment of the
>> function if what is provided is a function as second argument). It seems
>> to me that such a solution would enable fine granularity attenuated
>> authority at a very small cost:
>> ------
>> // codeInString:
>> localStorage.set('a', 1);
>> var a = localStorage.get('a');
>> localStorage.set('b', 2);
>>
>> // ...
>> var codeInString = // code above, but as a string
>> var chosenKey;
>> var oneSlotLocalStorage = {
>>  get: function(key){
>>     if(typeof chosenKey === 'string')
>>         return localStorage.get(chosenKey, val);
>>  }
>>  set: function(key, val){
>>    if(!chosenKey || chosenKey === key){
>>        chosenKey = key;
>>        localStorage.set(chosenKey, val);
>>    }
>>    else{
>>        throw new Error('You're not allowed to use more than one key!')
>>    }
>>  }
>> }
>>
>> eval(codeInString, {localStorage:oneSlotLocalStorage});
>> // 'a' is chosen as the key and an error is thrown when trying to set 'b'.
>> -----
>> Of course, it's a dummy example (and the second eval argument doesn't
>> exist), but it seems that this kind of attenuation will be far cheaper
>> (especially when direct proxies will be deployed) than iframes which
>> always recreate an entire environment (ECMAScript built-ins, DOM
>> built-ins, a document instance, etc.)
> Well I have a soft spot for eval(), but your comparison seems bizarre
> to me. The codeInString would not have access to ECMAScript built-ins,
> DOM built-ins, a document instance, etc. So sure the result will have
> better performance, the only problem is it also can't do anything
> useful.
In my example, no it doesn't, but it would be quite easy to set up a
forwarding proxy to my own global object and add restrictions to the
APIs I wish to put restrictions on.
This way, in a couple of lines, the untrusted code woud have access to
my environment minus the APIs I have specifically attenuated (which
could range from all of them to none of them, I have the freedom to
choose the granularity I want)

I'd like to point out that this design also provide a form of security
that iframes cannot provide. Same origin iframes have the access to the
same localStorage instance(thanks to security based on same origin), but
that can be quite a problem when, for instance, a school decides that
student addresses will be of the form http://myschool.org/~student. In
these URL layouts, visiting the page of a student means that the student
can steal anything in the local storage, even if you've opened the
student page in an iframe.

However, if you run the (potentially malicious) student code with an
attenuated localStorage API, the same student code becomes harmless.

David


More information about the es-discuss mailing list