[whatwg] The choice of script global object to use when the script element is moved

On Fri, 3 Sep 2010, Henri Sivonen wrote:
>
> When evaluating a parser-inserted script, there are three potential script global objects to use:
>  1) The script global object of the document whose active parser the parser that inserted the script is.
>  2) The script global object of the document that owned the script element at the time of invoking the "run" algorithm.
>  3) The script global object of the document that owns the script element at the time of script evaluation.

#1 and #2 are dodgy because the documents in question might have been 
GC'ed by that point.


> The spec says the answer is #3. WebKit (with HTML5 parser or without) 
> says the answer is #1. Firefox 3.6 says the answer is #2.
> 
> I doubt that there are Web compat considerations forcing this choice, 
> because IE8 doesn't get as far as running the script in this case. IE9 
> tries to do either #2 or #3 (not sure which) succeeding for inline 
> scripts and failing for external ones. (IIRC, the text in the spec that 
> explains the distinction between 1 and the other (without explaining the 
> distinction between 2 and 3) was added specifically for the benefit of 
> the IE team.)

I'm not sure exactly which sentence you mean here, but I'm happy to 
clarify text if anything is ambiguous.


> The spec asserts that these options are equally safe, because if 
> something is able to move the scripts so that 1, 2 and 3 would result in 
> different script global objects, the script gets moved within one 
> Origin.

There's some weirdness, e.g. if one of the browsing contexts has script 
disabled or if document.domain gets changed after the <script> node is 
moved to another document, but yeah, as far as I can tell either option is 
safe.


> However, if there's something other than Same Origin restricting what 
> scripts are eligible for evaluation (e.g. Content Security Policies that 
> I don't know well enough to reason about), 1, 2 and 3 might not be 
> equally safe.

Essentially, there are two browsing contexts, the one with the parser and 
the other one.

- If the one with the parser moves the script to the other context: for an 
attack to be relevant here, the script would have to run in the other 
context, but any attack possible this way could also be done by just 
creating a script in that document and appending it, or setting onload or 
onclick or some such content attribute in that document. If script is 
disabled in that other browsing context then nothing happens.

- If the one without the parser grabs the script and inserts it into 
itself, then for any attack to be interesting, scripting in the parser doc 
has to be disabled (since otherwise the other doc could just inject script 
into the parser doc and do whatever it wanted as if it was itself the 
parser doc). If we make scripts run in the parser doc context, then you 
can use this to grab a <script src=""> that is Referer-checked and execute 
it in the other doc's context, grabbing any information from that script. 
However, you could also do this by just pushState()ing to the other doc's 
URL, and then obtaining the script directly. The other possibility is 
scripts running in the non-parser doc context, but as far as I can tell 
you can always just grab the script from the other DOM and copy it to run 
in the non-parser doc, so again no new security problem seems to be 
introduced.

So in conclusion, I really don't see a new attack vector regardless of 
what we do here.


>  * Why does the spec say #3 when none of the browsers did #3 at the time 
> of spec writing?

I don't know the original reason. I would guess it was simply a matter of 
doing the simplest thing in the spec -- I try wherever possible to not 
refer back to the active parser if I can avoid it, letting things work 
identically with DOM manipulation from script as from the parser. Also, it 
avoids any weirdness like how to handle the case of the original doc(s) 
being GCed.


>  * Are there use cases that favor any one of these in particular? (I 
> doubt it.)

They seem identical.


> FWIW, my gut says we should do #1, since it is obviously secure, except 
> it would be unfortunate if the spec changed to #1 but too late for IE9 
> to match.

They all seem obviously secure. If you can manipulate a document's DOM, 
you can essentially do anything including running arbitrary script in that 
document or run script from that document in your document.

One advantage of doing #3 is that, as Adam pointed out, the implementation 
is required to get the security context at the last minute, where it's 
most likely to be up to date (e.g. with document.domain changes, etc) even 
in the case of the <script> element not being moved around.


On Fri, 3 Sep 2010, Boris Zbarsky wrote:
> 
> Could it cause script to run from a <script> element that someone sticks 
> in a same-origin but sandboxed iframe if the non-sandboxed parent moves 
> some part of the DOM out before the parse is done?

The only relevant case would be a sandboxed frame that is marked 
same-origin with script disabled, where a page moves a <script> block from 
the <iframe> into itself between the <script> being added to the DOM and 
the <script> executing. Yes, if you grab the <script> element before the 
parser hits the </script> tag in this particular case, you would end up 
running script that would otherwise not run. For this to be used as an 
attack vector you'd have to find a page that move nodes from a sandboxed 
iframe first, but if you did it seems like a much more interesting attack 
vector would be to just set event handler attributes on the nodes (onload, 
onmouseover, etc) that get copied.


On Tue, 7 Sep 2010, Henri Sivonen wrote:
> 
> Since the check "If scripting is disabled for the script element, or if 
> the user agent does not support the scripting language given by the 
> script block's type for this script element, then the user agent must 
> abort these steps at this point. The script is not executed." happens at 
> the time of the "run" algorithm and since iframe sandboxing or Content 
> Security Policies can cause scripting to be "disabled", a security check 
> has to happen at the time of invoking the "run" algorithm (assuming we 
> don't want to change the pre-existing behavior of what happens in the 
> common same-document case where a script gets rejected and we don't want 
> to decouple the time on supported language check from the time of 
> security-based rejections; this would be detectable in the 
> document.write() case).

Checking whether the browsing context's scripting is disabled is (at least 
in principle) a different check than getting the script's origin and 
global scope object, which is what matters in the execution case.


> Is there any good reason (other than differing from current IE9 PP 
> behavior) not to do #1 with the additional stipulation that making the 
> document whose active parser the parser is go away makes the scripts 
> that are pending to run in the context of its script global object 
> behave (stop?) the same regardless of which document they are in? (I.e. 
> if the document that had the active parser gets torn down before the 
> scripts inserted into another doc have loaded, those scripts wouldn't be 
> evaluated.) I still believe doing #1 in Gecko would be the simplest 
> thing. With the test cases above, WebKit seems to be doing #1 already 
> (and then crashing) and Opera fails to move the scripts so the execution 
> context ends up being the same as it would in case #1.

#3 is simpler to understand, IMHO. However, #1 is not particularly hard to 
spec, and I guess does decrease the odds of scripts being made to run 
accidentally by a bug in a script using sandboxed or CSP context.

If everyone is ok with #1, I'm happy to change the spec accordingly.


> Suppose there are two docs from one Origin. The document that the parser 
> is associated with doesn't have a CSP. A script in it moves a node in 
> such a way that the parser ends up inserting subsequent scripts into 
> another document. That document has a CSP that bans scripts. Would you 
> consider it a bug if a script ran in the context of the script global 
> object of the document whose CSP says no scripts?

If the CSP applied then the script would not run, if it didn't then it 
would run in the context of the doc without the CSP. I'm sure we wouldn't 
want to define the CSP as applying to nodes in a different way than the 
global scope is picked.

The more interesting case is the reverse: a non-CSP doc grabs <script> 
nodes from a CSP-covered partly-untrusted document being parsed, and ends 
up executing the scripts under a different policy.

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

Received on Wednesday, 8 September 2010 14:47:31 UTC