How to ensure that your script runs first in a webpage

# David Bruant (13 years ago)

I have claimed here a couple of times, that in a JavaScript application containing code from different parties, the first to run is the one that is in position to make decisions about security of the overall application (freezing the primordials for a defender or monkey-patching them if you're an attacker). I still have no proof (I feel it's coming though) about it, but a strong intuition.

Assuming this is true, then, on the web, one has to make sure that her protecting script runs first. How to ensure this, though? There is always a risk that with an XSS an attacker scripts runs before the protecting one. I think I have found an answer and it is: with Content Security Policy (CSP) [1].

CSP introduces a "script-src" directive [2] allowing only a whitelist of script URLs to be "fetchable" as script at src. Moreover, by default, inline scripts (in scripts or as on* attributes) won't execute.

Consequently, in browsers that support the script-src CSP directive (script whitelisting even reduced to one element and the "If 'unsafe-inline' is not in allowed script sources" rule), one can enforce running her script first. The restriction is even stronger, because the whitelisted script is just the only one to run.

It has to be noted that it does not limit the scripts that /can/ be run on the page since the unique script can still download other scripts and eval them. If a raw eval sounds too unsafe, you can embed in your unique script a JavaScript rewriter [3] ;-)

Browser support: Firefox 4+, Webkit-based browsers, IE10.

David

[1] dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html [2] dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#script-src [3] code.google.com/p/google-caja

# John J Barton (13 years ago)

On Wed, Feb 1, 2012 at 2:41 PM, David Bruant <bruant.d at gmail.com> wrote:

Hi,

I have claimed here a couple of times, that in a JavaScript application containing code from different parties, the first to run is the one that is in position to make decisions about security of the overall application (freezing the primordials for a defender or monkey-patching them if you're an attacker). I still have no proof (I feel it's coming though) about it, but a strong intuition.

Assuming this is true, then, on the web, one has to make sure that her protecting script runs first. How to ensure this, though? There is always a risk that with an XSS an attacker scripts runs before the protecting one. I think I have found an answer and it is: with Content Security Policy (CSP) [1].

Perhaps you can help me understand your reasoning here. To me, you have indeed "found the answer" and the "one that is in position to make decisions about security of the overall application" is in fact the browser the implements CSP.

I guess you have some use case in mind that you might share. It seems to me if you don't want a script to load, then don't load it. But somehow you want to load this attacker then prevent it from being successful?

jjb

# David Bruant (13 years ago)

Le 02/02/2012 00:02, John J Barton a écrit :

On Wed, Feb 1, 2012 at 2:41 PM, David Bruant<bruant.d at gmail.com> wrote:

Hi,

I have claimed here a couple of times, that in a JavaScript application containing code from different parties, the first to run is the one that is in position to make decisions about security of the overall application (freezing the primordials for a defender or monkey-patching them if you're an attacker). I still have no proof (I feel it's coming though) about it, but a strong intuition.

Assuming this is true, then, on the web, one has to make sure that her protecting script runs first. How to ensure this, though? There is always a risk that with an XSS an attacker scripts runs before the protecting one. I think I have found an answer and it is: with Content Security Policy (CSP) [1]. Perhaps you can help me understand your reasoning here. To me, you have indeed "found the answer" and the "one that is in position to make decisions about security of the overall application" is in fact the browser the implements CSP.

The context I'm placing myself in is the one of a web application author. As such, I rely on the web browser to ensure some security properties (tab isolation, standard compliance, for instance)... as well as the browser relies on the operating system to ensure some security properties (process isolation, for instance)... as well as the operating system relies on the hardware... Somewhere, there is also the network and a hope that there is no DNS spoofing in the network the browser is running

Before CSP, some "security-impacting" properties that the browser was providing when it came to script execution were :

  • scripts can be loaded from anywhere
  • there are inline scripts which execution can't be prevented In before-CSP platforms, as a web author, my trusted script is no more powerful than any other (potentially untrusted) script. This non-distinctive situation is what led to XSS being possible.

Within a CSP-enabled platform, as a web author, I can decide that only some scripts are granted full authority and based on that reduce the authority of additional scripts.

The browser does not make decisions about the security of my application, it only provides some security properties I can rely on to ensure the security of my application. And the security properties that CSP-enabled browsers provide are a game-changer.

I guess you have some use case in mind that you might share. It seems to me if you don't want a script to load, then don't load it. But somehow you want to load this attacker then prevent it from being successful?

Things are not that easy. Trust can be partial sometimes. In some cases, the "attacker" is an ad provider. You want it to be able to display the ad (because you need money to pay for food), but not allow the ad script to mess with the rest of your page (because there might be other ads providers in the same page and they give you money too!).

Another example: I heard that MDN wishes people to be able to send their applications so they can test web technologies quickly (very much like W3Schools's tryit [1]). This is possible securely without iframes within a CSP-enabled browser.

What I want is to load a potentially untrusted script and grant it the authority to do its job and nothing more. In some places, they call this "applying POLA" (Principle Of Least Authority), because you provide to some script the least authority it needs to do its job.

David

[1] www.w3schools.com/js/tryit.asp?filename=tryjs_events [2] en.wikipedia.org/wiki/Principle_of_least_privilege

# Russell Leggett (13 years ago)

On Wed, Feb 1, 2012 at 5:41 PM, David Bruant <bruant.d at gmail.com> wrote:

Hi,

I have claimed here a couple of times, that in a JavaScript application containing code from different parties, the first to run is the one that is in position to make decisions about security of the overall application (freezing the primordials for a defender or monkey-patching them if you're an attacker). I still have no proof (I feel it's coming though) about it, but a strong intuition.

Assuming this is true, then, on the web, one has to make sure that her protecting script runs first. How to ensure this, though? There is always a risk that with an XSS an attacker scripts runs before the protecting one. I think I have found an answer and it is: with Content Security Policy (CSP) [1].

CSP introduces a "script-src" directive [2] allowing only a whitelist of script URLs to be "fetchable" as script at src. Moreover, by default, inline scripts (in scripts or as on* attributes) won't execute.

Consequently, in browsers that support the script-src CSP directive (script whitelisting even reduced to one element and the "If 'unsafe-inline' is not in allowed script sources" rule), one can enforce running her script first. The restriction is even stronger, because the whitelisted script is just the only one to run.

I agree with (I think) all of what you've said here. Running first is absolutely the key to being able to lock down and protect yourself. CSP can clearly help you guarantee that, plus a lot more. What I don't quite understand is the level of emphasis being placed here. CSP is not guaranteed in all browsers, so you can't rely on it - its not exactly something you can polyfill, and beyond that, it just seems like in practice, it is unlikely to make the difference between running first and not running first.

Any developer who cares enough, and knows enough, to use CSP and some script to lock down the browser, will surely be able to protect themselves from the tiny attack vector available if you just put the lockdown script first in your head tag. I think I said this before and you mentioned the title tag. Looking at the spec and testing, I noticed a few things.

Lets assume an attack where the coder allowed an unescaped variable into the the title tag like:

<title>${unescapedContentFromMaliciousUser}</title>
  1. You can put a script tag above the title tag. This is perfectly fine according to spec, even though it is not common practice.

  2. You cannot put a script tag inside the title tag - it will not get executed. This is also according to spec and I've tested it.

  3. If the attacker tried to do a sql injection style attack and close the <title> and follow it with a script tag like:

    <title></title><script type="text/javascript">console.log("I'm

attacking!");</script></title>

It will execute, but as long as you put your protection script first, yours will run first.

Ultimately, while I think there is value in CSP, for sure, in this case isn't it just easier to put your protection script all the way at the top? I would be happy to see a counter-example.

# David Bruant (13 years ago)

Le 02/02/2012 15:34, Russell Leggett a écrit :

On Wed, Feb 1, 2012 at 5:41 PM, David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:

Hi,

I have claimed here a couple of times, that in a JavaScript
application
containing code from different parties, the first to run is the
one that
is in position to make decisions about security of the overall
application (freezing the primordials for a defender or
monkey-patching
them if you're an attacker). I still have no proof (I feel it's coming
though) about it, but a strong intuition.

Assuming this is true, then, on the web, one has to make sure that her
protecting script runs first. How to ensure this, though? There is
always a risk that with an XSS an attacker scripts runs before the
protecting one.
I think I have found an answer and it is: with Content Security Policy
(CSP) [1].

CSP introduces a "script-src" directive [2] allowing only a
whitelist of
script URLs to be "fetchable" as script at src. Moreover, by default,
inline scripts (in scripts or as on* attributes) won't execute.

Consequently, in browsers that support the script-src CSP directive
(script whitelisting even reduced to one element and the "If
'unsafe-inline' is not in allowed script sources" rule), one can
enforce
running her script first.
The restriction is even stronger, because the whitelisted script
is just
the only one to run.

I agree with (I think) all of what you've said here. Running first is absolutely the key to being able to lock down and protect yourself. CSP can clearly help you guarantee that, plus a lot more. What I don't quite understand is the level of emphasis being placed here. CSP is not guaranteed in all browsers, so you can't rely on it - its not exactly something you can polyfill

That's why I included the browser support at the end of my initial message (you stripped that part)

and beyond that, it just seems like in practice, it is unlikely to make the difference between running first and not running first.

What is unlikely to make the difference?

Any developer who cares enough, and knows enough, to use CSP and some script to lock down the browser, will surely be able to protect themselves from the tiny attack vector available if you just put the lockdown script first in your head tag.

You're making me realize that in (CSP-disabled) browsers, running first may not be enough. Considering inline scripts, these can run after you, but can be hurtful anyway. For instance, even if you run first, you can't prevent later inline scripts from accessing the "document" property of the global object (it is non-configurable and the spec is the one saying so [1])

I think I said this before and you mentioned the title tag. (snip)

Regardless of before of after the title tag, if we want run first, it seems necessary (in CSP-disabled browsers) to have an inline synchronous <script>. This is annoying for performance. However, it seems that CSP

allows not only to safely run any JavaScript in the page, but to do it with performance as well (actually, a web browser, when seeing only one URL in the script-src directive could decide to fetch the js file even before the HTML has finished being downloaded).

Ultimately, while I think there is value in CSP, for sure, in this case isn't it just easier to put your protection script all the way at the top? I would be happy to see a counter-example.

Inline scripts which have access to the document object (which can easily be ab-used for phishing)?

David

[1] www.whatwg.org/specs/web-apps/current-work/multipage/browsers.html#the-window-object 'document' property is [unforgeable]

# Russell Leggett (13 years ago)

You're making me realize that in (CSP-disabled) browsers, running first may not be enough. Considering inline scripts, these can run after you, but can be hurtful anyway. For instance, even if you run first, you can't prevent later inline scripts from accessing the "document" property of the global object (it is non-configurable and the spec is the one saying so [1])

Yes, I agree with you here, I was just contending that CSP should not be required to be able to run first.

I think I said this before and you mentioned the title tag. (snip)

Regardless of before of after the title tag, if we want run first, it seems necessary (in CSP-disabled browsers) to have an inline synchronous <script>. This is annoying for performance. However, it seems that CSP allows not only to safely run any JavaScript in the page, but to do it with performance as well (actually, a web browser, when seeing only one URL in the script-src directive could decide to fetch the js file even before the HTML has finished being downloaded).

This is an important point. I will concede that I like to put all my script tags at the bottom of the page for precisely this reason. Being able to put the protection script at the bottom for performance reasons and still guarantee running first would definitely be a win.

Ultimately, while I think there is value in CSP, for sure, in this case

isn't it just easier to put your protection script all the way at the top? I would be happy to see a counter-example.

Inline scripts which have access to the document object (which can easily be ab-used for phishing)?

As I said above, CSP provides additional protection that I'm happy to have, but as this thread is titled, "How to ensure that your script runs first in a webpage," that is what I was trying to debate. As long as I put my protection script as the first element of the head tag, is there any way that a malicious attacker could somehow run a script first. I think the answer is no. That is the counter-example I am looking for.

# John J Barton (13 years ago)

On Thu, Feb 2, 2012 at 12:51 AM, David Bruant <bruant.d at gmail.com> wrote:

Le 02/02/2012 00:02, John J Barton a écrit :

On Wed, Feb 1, 2012 at 2:41 PM, David Bruant<bruant.d at gmail.com>  wrote:

Things are not that easy. Trust can be partial sometimes. In some cases, the "attacker" is an ad provider. You want it to be able to display the ad (because you need money to pay for food), but not allow the ad script to mess with the rest of your page (because there might be other ads providers in the same page and they give you money too!).

Another example: I heard that MDN wishes people to be able to send their applications so they can test web technologies quickly (very much like W3Schools's tryit [1]). This is possible securely without iframes within a CSP-enabled browser.

What I want is to load a potentially untrusted script and grant it the authority to do its job and nothing more. In some places, they call this "applying POLA" (Principle Of Least Authority), because you provide to some script the least authority it needs to do its job.

Thanks for these use-cases. In my opinion they support my point: these uses cases are not compelling. They do not justify the huge overall cost of supporting security in the way you outline.

In both cases the API seen by the included page is not larger than the API of a normal web page. So an iframe is just the right match to the problem.

I'm not saying we can't do better, I am claiming that the impact of adding security features to the programming language is not (yet?) justified. There are better solutions based on iframes that do not require such large investments. In particular, systems like q-comm allow controlled API access between isolated JS environments.

jjb

# Mark S. Miller (13 years ago)

On Fri, Feb 3, 2012 at 7:36 AM, John J Barton <johnjbarton at johnjbarton.com>wrote: [...]

I'm not saying we can't do better, I am claiming that the impact of adding security features to the programming language is not (yet?) justified.

I must have missed something. What language change suggestions are you reacting to?

ES5 already supports SES and ES6 will as well, probably somewhat better. The "costs" were largely non-controversial and are behind us in any case.

There are better solutions based on iframes that do not require such large investments. In particular, systems like q-comm allow controlled API access between isolated JS environments.

I am (as you know) a big fan of q-comm and such Q libraries, as well as the communicating event loop model where iframe/worker like units only interact by asynchronous messages. These certainly have their place, and that place is huge.

However, I strongly disagree that iframes are a better security mechanism than the language-based mechanisms provided by SES. iframes are an unholy mess, and by design and specification (both old and HTML5) cannot support confinement. The best way to leverage the security that Q-like libraries can provide is to see them as extending SES out onto the network.

We can talk more about this offline if you'd like.

# John J Barton (13 years ago)

On Fri, Feb 3, 2012 at 8:12 AM, Mark S. Miller <erights at google.com> wrote:

On Fri, Feb 3, 2012 at 7:36 AM, John J Barton <johnjbarton at johnjbarton.com> wrote: [...]

I'm not saying we can't do better, I am claiming that the impact of adding security features to the programming language is not (yet?) justified.

I must have missed something. What language change suggestions are you reacting to?

ES5 already supports SES and ES6 will as well, probably somewhat better. The "costs" were largely non-controversial and are behind us in any case.

Well David seems to be building up to something, so I wanted to get some controversy out in front.

There are better solutions based on iframes that do not require such large investments. In particular, systems like q-comm allow controlled API access between isolated JS environments.

I am (as you know) a big fan of q-comm and such Q libraries, as well as the communicating event loop model where iframe/worker like units only interact by asynchronous messages. These certainly have their place, and that place is huge.

However, I strongly disagree that iframes are a better security mechanism than the language-based mechanisms provided by SES. iframes are an unholy mess, and by design and specification (both old and HTML5) cannot support confinement. The best way to leverage the security that Q-like libraries can provide is to see them as extending SES out onto the network.

iframes seem to be effective for the cases David outlined.

jjb

# David Bruant (13 years ago)

Le 03/02/2012 17:54, John J Barton a écrit :

On Fri, Feb 3, 2012 at 8:12 AM, Mark S. Miller <erights at google.com> wrote:

On Fri, Feb 3, 2012 at 7:36 AM, John J Barton <johnjbarton at johnjbarton.com> wrote: [...]

I'm not saying we can't do better, I am claiming that the impact of adding security features to the programming language is not (yet?) justified.

I must have missed something. What language change suggestions are you reacting to?

ES5 already supports SES and ES6 will as well, probably somewhat better. The "costs" were largely non-controversial and are behind us in any case.

Well David seems to be building up to something, so I wanted to get some controversy out in front.

For now, I wasn't suggesting any change in the language. It's just that on es-dicuss, I had said a couple of times that JavaScript security relied on whoever runs first and that I had found a way to guarantee it with CSP that I wanted to share. Discussion led to finding that in web browsers, running first, may not be enough sometimes, but the issue is solved with CSP anyway. Again, no need to ad something to ECMAScript. Actually my point was that in CSP-enabled browser, there is no need for anything else.

There are better solutions based on iframes that do not require such large investments. In particular, systems like q-comm allow controlled API access between isolated JS environments.

I am (as you know) a big fan of q-comm and such Q libraries, as well as the communicating event loop model where iframe/worker like units only interact by asynchronous messages. These certainly have their place, and that place is huge.

However, I strongly disagree that iframes are a better security mechanism than the language-based mechanisms provided by SES. iframes are an unholy mess, and by design and specification (both old and HTML5) cannot support confinement. The best way to leverage the security that Q-like libraries can provide is to see them as extending SES out onto the network. iframes seem to be effective for the cases David outlined.

I can come up with more use cases ;-) Regardless of use cases iframes will always have an overhead against just loading some code with attenuated authority.

For instance, if we want to create a game where participants submit some code to compete with each other. When loading the participants code, iframes can have a performance issue and asynchronous communication as done in the browser (with HTML5 cloning algorithm as the "richest form of sharing") may be very limitating. However, being able to load the code in the same frame ("vat" as said in the concurrency strawman) would certainly yield better performance. The only remaining threat would be DoS (both in memory and CPU time), an alternative allowing performance, rich interaction and preventing by design DoS is what is presented on the concurrency strawman. But we're not there and iframes are far from providing the same level of control.

# John J Barton (13 years ago)

On Fri, Feb 3, 2012 at 1:08 PM, David Bruant <bruant.d at gmail.com> wrote:

Le 03/02/2012 17:54, John J Barton a écrit :

iframes seem to be effective for the cases David outlined. I can come up with more use cases ;-) Regardless of use cases iframes will always have an overhead against just loading some code with attenuated authority.

To support the attenuation of authority you have to create a container (authority boundary) and you have to attenuate the API you give the loaded code. These activities are not free.

For the container, the important question (which I do not know the answer to): can the container be isolated from the rest of the Web app running in the window? Is there any impact on the rest of the application from the choice to allow untrusted code to be loaded into it?

To ask the same question in a more personal way: I've read code with PropertyDescriptor/freeze() etc. Will I have to write that kind of code?

For instance, if we want to create a game where participants submit some code to compete with each other. When loading the participants code, iframes can have a performance issue and asynchronous communication as done in the browser (with HTML5 cloning algorithm as the "richest form of sharing") may be very limitating.

Both technologies require the attenuated API. Since this API is the security hole, it will require a great deal of attention. I think in practical cases this effort will dwarf other issues.

However, being able to load the code in the same frame ("vat" as said in the concurrency strawman) would certainly yield better performance.

I thought this way as well only a few months ago. But the Chrome multi-process architecture convinced me to reconsider. Integration calls, the kind of API a security barrier can support, are just not performance sensitive by design. And JSON messaging performs very well.

The only remaining threat would be DoS (both in memory and CPU time), an alternative allowing performance, rich interaction and preventing by design DoS is what is presented on the concurrency strawman. But we're not there and iframes are far from providing the same level of control.

Will the security solution prevent the untrusted code from manipulating the DOM tree outside of a specific element?

(I appreciate your answers even if my questions sound pointed)

jjb

# David Bruant (13 years ago)

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:

Le 03/02/2012 17:54, John J Barton a écrit :

iframes seem to be effective for the cases David outlined. I can come up with more use cases ;-) Regardless of use cases iframes will always have an overhead against just loading some code with attenuated authority. To support the attenuation of authority you have to create a container (authority boundary) and you have to attenuate the API you give the loaded code. These activities are not free.

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.

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.)

For the container, the important question (which I do not know the answer to): can the container be isolated from the rest of the Web app running in the window? Is there any impact on the rest of the application from the choice to allow untrusted code to be loaded into it?

If the second argument of eval is what you call a container, then yes, you can fully isolate this code. The only threat remaning threats (besides bugs in your isolation code) is DoS (if the scripts decides to allocate infinite memory or to get stuck in an infinite loop).

To ask the same question in a more personal way: I've read code with PropertyDescriptor/freeze() etc. Will I have to write that kind of code?

Considering the idea of the eval with a second argument from above, the answer is: it's up to you. If you want to freeze your objects, do it. If you feel like passing attenuated APIs as I showed above is enough to enforce the security you need, then, don't freeze your objects.

For instance, if we want to create a game where participants submit some code to compete with each other. When loading the participants code, iframes can have a performance issue and asynchronous communication as done in the browser (with HTML5 cloning algorithm as the "richest form of sharing") may be very limitating. Both technologies require the attenuated API. Since this API is the security hole, it will require a great deal of attention. I think in practical cases this effort will dwarf other issues.

I'm not sure I understand. What do you call "the attenuated API". Especially for iframes, there is currently no API besides postMessage/onmessage event in the different-origin case.

However, being able to load the code in the same frame ("vat" as said in the concurrency strawman) would certainly yield better performance. I thought this way as well only a few months ago. But the Chrome multi-process architecture convinced me to reconsider. Integration calls, the kind of API a security barrier can support, are just not performance sensitive by design.

It's an overhead anyway. Maybe constant, maybe small, but that's an overhead.

And JSON messaging performs very well.

It requires a copy. Maybe it's not big, but it's an overhead you can avoid as well.

What I'm advocating here is the same security properties without the overheads. Maybe these overheads are small, but they exist by design.

The only remaining threat would be DoS (both in memory and CPU time), an alternative allowing performance, rich interaction and preventing by design DoS is what is presented on the concurrency strawman. But we're not there and iframes are far from providing the same level of control. Will the security solution prevent the untrusted code from manipulating the DOM tree outside of a specific element?

Considering the 2-argument eval example from above, you could define a 'document' property is the second argument and this document (what the eval code gets when it asks for the "document" variable) could be an emulation of an HTMLDocument, but forward all (relevant) calls (appendChild...) to a given element you've chosen.

It seems it could be easily implemented.

(I appreciate your answers even if my questions sound pointed)

I appreciate the questions, this discussion is enriching as far as I'm concerned.

David

[1] code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/startSES.js#513 [2] bugzilla.mozilla.org/show_bug.cgi?id=442333

# John J Barton (13 years ago)

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:

Le 03/02/2012 17:54, John J Barton a écrit :

iframes seem to be effective for the cases David outlined. I can come up with more use cases ;-) Regardless of use cases iframes will always have an overhead against just loading some code with attenuated authority. To support the attenuation of authority you have to create a container (authority boundary) and you have to attenuate the API you give the loaded code.  These activities are not free. 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(). This would true of all their properties as well, including Object, Function etc.

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.

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.

For the container, the important question (which I do not know the answer to): can the container be isolated from the rest of the Web app running in the window? Is there any impact on the rest of the application from the choice to allow untrusted code to be loaded into it? If the second argument of eval is what you call a container, then yes, you can fully isolate this code. The only threat remaning threats (besides bugs in your isolation code) is DoS (if the scripts decides to allocate infinite memory or to get stuck in an infinite loop).

To ask the same question in a more personal way: I've read code with PropertyDescriptor/freeze() etc. Will I have to write that kind of code? Considering the idea of the eval with a second argument from above, the answer is: it's up to you. If you want to freeze your objects, do it. If you feel like passing attenuated APIs as I showed above is enough to enforce the security you need, then, don't freeze your objects.

For instance, if we want to create a game where participants submit some code to compete with each other. When loading the participants code, iframes can have a performance issue and asynchronous communication as done in the browser (with HTML5 cloning algorithm as the "richest form of sharing") may be very limitating. Both technologies require the attenuated API. Since this API is the security hole, it will require a great deal of attention. I think in practical cases this effort will dwarf other issues. I'm not sure I understand. What do you call "the attenuated API". Especially for iframes, there is currently no API besides postMessage/onmessage event in the different-origin case.

You might take a look at q-comm. A little bit of JS wrapper code and you have RPC calls. The only real difference is that the these calls will be async, but that is probably what you want for untrusted code anyway.

However, being able to load the code in the same frame ("vat" as said in the concurrency strawman) would certainly yield better performance. I thought this way as well only a few months ago. But the Chrome multi-process architecture convinced me to reconsider. Integration calls, the kind of API a security barrier can support, are just not performance sensitive by design. It's an overhead anyway. Maybe constant, maybe small, but that's an overhead.

Sure, but your eval/lexical environment is overhead also.

And JSON messaging performs very well. It requires a copy. Maybe it's not big, but it's an overhead you can avoid as well.

What I'm advocating here is the same security properties without the overheads. Maybe these overheads are small, but they exist by design.

The only remaining threat would be DoS (both in memory and CPU time), an alternative allowing performance, rich interaction and preventing by design DoS is what is presented on the concurrency strawman. But we're not there and iframes are far from providing the same level of control. Will the security solution prevent the untrusted code from manipulating the DOM tree outside of a specific element? Considering the 2-argument eval example from above, you could define a 'document' property is the second argument and this document (what the eval code gets when it asks for the "document" variable) could be an emulation of an HTMLDocument, but forward all (relevant) calls (appendChild...) to a given element you've chosen.

It seems it could be easily implemented.

I'd guess not, but overall I think the restricted eval or similar system sounds interesting to explore.

jjb

# Mark S. Miller (13 years ago)

On Fri, Feb 3, 2012 at 4:14 PM, John J Barton <johnjbarton at johnjbarton.com>wrote:

On Fri, Feb 3, 2012 at 3:14 PM, David Bruant <bruant.d at gmail.com> wrote:

[...]

You might take a look at q-comm. A little bit of JS wrapper code and you have RPC calls. The only real difference is that the these calls will be async, but that is probably what you want for untrusted code anyway.

Sometimes yes, sometimes no. Now that we have SES[1], we can easily support the cases where an asynchrony boundary in inappropriate. For example, say you wish wish to load some third party library in order to make use of some of its features. But the features you want to use do not require access to the real DOM, XHR, or cookies. You can now load the library, give it what you think it should have an no more, and if it still works (many do), you can proceed to use it without entrusting it with access to those resources.

However, being able to load the code in the same frame ("vat" as said in the concurrency strawman) would

certainly yield better performance. I thought this way as well only a few months ago. But the Chrome multi-process architecture convinced me to reconsider. Integration calls, the kind of API a security barrier can support, are just not performance sensitive by design. It's an overhead anyway. Maybe constant, maybe small, but that's an overhead.

Sure, but your eval/lexical environment is overhead also.

Please measure it across browsers. I think you'll find the overhead is pleasingly small. The reason is that SES does not translate the untrusted code in order to confine it. Since it is not translated, it runs at essentially full speed. (Though see[2].)

And JSON messaging performs very well. It requires a copy. Maybe it's not big, but it's an overhead you can avoid as well.

Care to benchmark a JSON postMessage between address spaces vs a direct method invocation among objects sharing a heap?

Considering the 2-argument eval example from above, you could define a

'document' property is the second argument and this document (what the eval code gets when it asks for the "document" variable) could be an emulation of an HTMLDocument, but forward all (relevant) calls (appendChild...) to a given element you've chosen.

It seems it could be easily implemented.

Hi David, SES implements essentially your two argument eval, but instead of

eval(codeInString, {localStorage:oneSlotLocalStorage});

you'd say

var imports = cajaVM.makeImports();
cajaVM.copyToImports(imports, {localStorage:oneSlotLocalStorage});
cajaVM.compileExpr(codeInString)(imports);

As for your suggestion of providing a virtual document as the binding of "document" seen by untrusted code, that is precisely how Caja uses SES. The Caja wrapping and virtualization of the browser API (especially the DOM) is known as Domado[3].

I'd guess not, but overall I think the restricted eval or similar system sounds interesting to explore.

Please do! It works.

[1] < code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses

research.google.com/pubs/archive/37199.pdf, www.youtube.com/watch?v=w9hHHvhZ_HY, www.youtube.com/watch?v=oBqeDYETXME < sites.google.com/site/ladameeting/preparing-for-the-workshop/ladapapers/lada-js.pdf> <=== this one is new

[2] code.google.com/p/google-caja/wiki/SES

[3] code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/plugin/domado.js?r=4758

# David Bruant (13 years ago)

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 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 Bruant (13 years ago)

Le 04/02/2012 04:42, Mark S. Miller a écrit :

Considering the 2-argument eval example from above, you could define a

> 'document' property is the second argument and this document
(what the
> eval code gets when it asks for the "document" variable) could be an
> emulation of an HTMLDocument, but forward all (relevant) calls
> (appendChild...) to a given element you've chosen.
>
> It seems it could be easily implemented.

Hi David, SES implements essentially your two argument eval, but instead of

eval(codeInString, {localStorage:oneSlotLocalStorage});

you'd say

var imports = cajaVM.makeImports();
cajaVM.copyToImports(imports, {localStorage:oneSlotLocalStorage});
cajaVM.compileExpr(codeInString)(imports);

I had some insight that there was a way, but wasn't sure of how to do it in Caja, thanks.

The internalCompileExpr function uses "with", how will this code behave in ES6 since it's built on top of ES5 strict?

Do you think there would be a value in having the 2-arguments eval as a native construct? Maybe performance? Maybe long-term reliability if there is a test suite in test262 that browsers will be strongly encouraged to comply to?

As for your suggestion of providing a virtual document as the binding of "document" seen by untrusted code, that is precisely how Caja uses SES. The Caja wrapping and virtualization of the browser API (especially the DOM) is known as Domado[3].

[3] code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/plugin/domado.js?r=4758

Thanks for the reference.

# Mark S. Miller (13 years ago)

On Sat, Feb 4, 2012 at 3:48 AM, David Bruant <bruant.d at gmail.com> wrote: [...]

The internalCompileExpr function uses "with", how will this code behave in ES6 since it's built on top of ES5 strict?

a) ES6 will still support non-strict code. An indirect ES6 eval (as used here) will still eval non-strict as long as the string being evaluated doesn't start with "use strict";. The strictness of the caller of an indirect eval doesn't matter. So the existing SES code should work securely on an ES6 system, as far as we can tell.

b) Even the lightweight scanning we're currently doing on ES5 to pick up all potential free variable names will be unnecessary in ES6, since we can just do "with (proxy) {".

c) The ES6 module loader should make all these with-games unnecessary anyhow, since it gives us a principled approach for controlling the top level scope of untrusted code. Long term, this is the real answer.

# David Bruant (13 years ago)

Le 04/02/2012 18:14, Mark S. Miller a écrit :

c) The ES6 module loader should make all these with-games unnecessary anyhow, since it gives us a principled approach for controlling the top level scope of untrusted code. Long term, this is the real answer.

Ok, good to know. Can you show how "eval(string, lexEnv)" example would look like with module loaders? The proposal [1] (and the entire web?) lacks a bit of examples.

Thanks,

David

[1] harmony:module_loaders

# Sam Tobin-Hochstadt (13 years ago)

On Sat, Feb 4, 2012 at 1:02 PM, David Bruant <bruant.d at gmail.com> wrote:

Le 04/02/2012 18:14, Mark S. Miller a écrit :

c) The ES6 module loader should make all these with-games unnecessary anyhow, since it gives us a principled approach for controlling the top level scope of untrusted code. Long term, this is the real answer. Ok, good to know. Can you show how "eval(string, lexEnv)" example would look like with module loaders? The proposal [1] (and the entire web?) lacks a bit of examples.

There's an examples page which includes some module loaders examples (although not this one) [1].

I'm imagining that |lexEnv| is the entire environment that you want available for this evaluation, ie, it shouldn't see any other bindings. For this, we'd do the following:

let l = new Loader(system /* the parent loader /, null / make fresh intrinsics /, lexEnv / use lexEnv as the global scope */); l.eval(string);

Note that this is slightly different than the API currently described on the web page; Dave and I have updates that we need to make there.

[1] harmony:modules_examples