Intent to ship: Make iframe.contentWindow inaccessible while iframe is loading.

458 views
Skip to first unread message

Yuki Shiino

unread,
Jan 27, 2015, 3:53:13 AM1/27/15
to blink-dev, Kentaro Hara, dgo...@chromium.org, Jochen Eisinger, pfel...@chromium.org, mk...@chromium.org, aba...@chromium.org
Contact emails

Spec

Summary
Makes iframe.contentWindow return null until iframe gets loaded.  iframe.contentWindow is inaccessible from JS until iframe.onload fires.

Motivation
This change is needed to fix http://crbug.com/448314 (this issue is visible only for limited people because it's a security issue).
If iframe.contentWindow (thus iframe.contentWindow.document) is accessible from JS before iframe gets loaded, JS can insert arbitrary code which can run while document is being created, especially that JS can destroy the frame and document which are being loaded.  This is a security risk.

Compatibility Risk
Firefox: iframe.contentWindow return null while iframe is being loaded.
Internet Explorer: iframe.contentWindow.document return null while iframe is being loaded.
Safari: Both of iframe.contentWindow and iframe.contentWindow.document are available while iframe is being loaded.

Note that Window.document is not nullable while Iframe.contentWindow is nullable.  So IE's behavior seems not conforming to the spec.

Yuki Shiino

Kentaro Hara

unread,
Jan 27, 2015, 4:00:33 AM1/27/15
to Yuki Shiino, blink-dev, Dmitry Gozman, Jochen Eisinger, Pavel Feldman, Mike West, aba...@chromium.org
non-owner LGTM


--
Kentaro Hara, Tokyo, Japan

Daniel Cheng

unread,
Jan 27, 2015, 4:24:11 AM1/27/15
to Kentaro Hara, Yuki Shiino, blink-dev, Dmitry Gozman, Jochen Eisinger, Pavel Feldman, Mike West, aba...@chromium.org
Do you mind elaborating on what you mean by loading?

Example 1:
var f = document.createElement('iframe');
document.body.appendChild(f);
console.log(f.contentWindow);

Example 2:
var f = document.createElement('iframe');
document.body.appendChild(f);
console.log(f.contentWindow);

Example 3:
var f = document.createElement('iframe');
f.onload = function () {
  f.src = 'https://example.com/2';
  console.log(f.contentWindow);
}

What would be the console output in these 3 examples?

Daniel

Philip Jägenstedt

unread,
Jan 27, 2015, 4:32:43 AM1/27/15
to Yuki Shiino, blink-dev, Kentaro Hara, dgo...@chromium.org, Jochen Eisinger, Pavel Feldman, Mike West, Adam Barth
I'm unable to find the bits in the spec that say when iframe.contentWindow should be null. Starting at https://html.spec.whatwg.org/#dom-iframe-contentwindow (the W3C copy is older than the date suggests) and following links I see nothing obvious. It seems like it should be somewhere around https://html.spec.whatwg.org/#browsers but I can't find it. Help?

BTW, I assume you also mean to change HTMLFrameElement.contentWindow in the same way?

Philip

To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.

Yuki Shiino

unread,
Jan 27, 2015, 8:10:40 AM1/27/15
to Daniel Cheng, Kentaro Hara, Yuki Shiino, blink-dev, Dmitry Gozman, Jochen Eisinger, Pavel Feldman, Mike West, aba...@chromium.org
2015-01-27 18:24 GMT+09:00 Daniel Cheng <dch...@chromium.org>:
Do you mind elaborating on what you mean by loading?

My plan is make iframe return non-null when 'onload' event is about to fire for the iframe.  'onload' event handlers of the iframe can see iframe.contentWindow.  Before that timing, iframe.contentWindow returns null.


Example 1:
var f = document.createElement('iframe');
document.body.appendChild(f);
console.log(f.contentWindow);

f.contentWindow is null.

Example 2:
var f = document.createElement('iframe');
document.body.appendChild(f);
console.log(f.contentWindow);

f.contentWindow is null.

Example 3:
var f = document.createElement('iframe');
f.onload = function () {
  f.src = 'https://example.com/2';
  console.log(f.contentWindow);
}

I'm not sure on this point, however, IIUC, f.contentWindow has not yet replaced at this point (the loading of 'example.com/2' has not yet started), so f.contentWindow returns the Window object with the content of 'example.com'.

If f.contentWindow should return the Window object with the content of 'example.com/2', then f.contentWindow returns null.

Cheers,
Yuki Shiino

Yuki Shiino

unread,
Jan 27, 2015, 8:20:30 AM1/27/15
to Philip Jägenstedt, Yuki Shiino, blink-dev, Kentaro Hara, Dmitry Gozman, Jochen Eisinger, Pavel Feldman, Mike West, Adam Barth
I wasn't unable to find a section describing the exact timing when iframe.contentWindow should be non-null, too.

The spec says: The contentWindow IDL attribute must return the WindowProxy object of the iframe element's nested browsing context, if any, or null otherwise.

So, if the nested browsing context or the WindowProxy object is not yet well-constructed nor ready to be accessible from JS, it seems okay to return null.


For HTMLFrameElement.contentWindow, yes, I'm planning to make the same change for HTMLFrameElement.contentWindow.  Both of them are implemented as HTMLFrameOwnerElement::contentWindow.

Cheers,
Yuki Shiino

Simon Pieters

unread,
Jan 27, 2015, 8:35:17 AM1/27/15
to Philip Jägenstedt, Yuki Shiino, blink-dev, Kentaro Hara, Dmitry Gozman, Jochen Eisinger, Pavel Feldman, Mike West, Adam Barth
On Tue, 27 Jan 2015 14:19:47 +0100, Yuki Shiino <yukis...@chromium.org>
wrote:

> I wasn't unable to find a section describing the exact timing when
> iframe.contentWindow should be non-null, too.
>
> The spec says: The contentWindow IDL attribute must return the
> WindowProxy
> object of the iframe element's nested browsing context, if any, or null
> otherwise.
>
> So, if the nested browsing context or the WindowProxy object is not yet
> well-constructed nor ready to be accessible from JS, it seems okay to
> return null.

No, you need to also look at where the spec says that an iframe has a
browsing context or not.

[[
When an iframe element is inserted into a document that has a browsing
context, the user agent must create a nested browsing context, and then
process the iframe attributes for the "first time".

When an iframe element is removed from a document, the user agent must
discard the nested browsing context, if any.
]]
https://html.spec.whatwg.org/multipage/embedded-content.html#the-iframe-element:the-iframe-element-7

"process the iframe attributes" then navigates the browsing context:

https://html.spec.whatwg.org/multipage/embedded-content.html#process-the-iframe-attributes

--
Simon Pieters
Opera Software

Daniel Cheng

unread,
Jan 27, 2015, 9:17:20 AM1/27/15
to Yuki Shiino, Kentaro Hara, blink-dev, Dmitry Gozman, Jochen Eisinger, Pavel Feldman, Mike West, aba...@chromium.org
On Tue Jan 27 2015 at 5:10:37 AM Yuki Shiino <yukis...@chromium.org> wrote:
2015-01-27 18:24 GMT+09:00 Daniel Cheng <dch...@chromium.org>:
Do you mind elaborating on what you mean by loading?

My plan is make iframe return non-null when 'onload' event is about to fire for the iframe.  'onload' event handlers of the iframe can see iframe.contentWindow.  Before that timing, iframe.contentWindow returns null.


Example 1:
var f = document.createElement('iframe');
document.body.appendChild(f);
console.log(f.contentWindow);

f.contentWindow is null.

Example 2:
var f = document.createElement('iframe');
document.body.appendChild(f);
console.log(f.contentWindow);

f.contentWindow is null.

From https://html.spec.whatwg.org/multipage/embedded-content.html#process-the-iframe-attributes (we actually do this step synchronously instead of queuing a task to do it):

Otherwise, if the element has no src attribute specified, and the user agent is processing the iframe's attributes for the "first time"
 -> Queue a task to run the iframe load event steps.

In addition, all browsing contexts begin at the initial empty document that is completely loaded (from https://html.spec.whatwg.org/multipage/browsers.html#windows) at the creation of the context.

Given this, I think the iframe must be considered fully loaded in both examples 1 and 2. In addition, Firefox appears to return a Window object while the iframe is still on the initial empty document.

Boris Zbarsky

unread,
Jan 27, 2015, 10:31:48 AM1/27/15
to Yuki Shiino, blink-dev
On 1/27/15 3:52 AM, Yuki Shiino wrote:
> Firefox: iframe.contentWindow return null while iframe is being loaded.

I'm not aware of any such behavior in Firefox. What testcase did you
use to determine this, exactly?

-Boris

P.S. The obvious testcase:

data:text/html,<body><script>var i = document.createElement("iframe");
i.src="http://www.w3.org/"; document.body.appendChild(i);
alert(i.contentWindow);</script>

does not show null in Firefox, unsurprisingly.

PhistucK

unread,
Jan 27, 2015, 11:55:02 AM1/27/15
to Yuki Shiino, blink-dev, Kentaro Hara, dgo...@chromium.org, Jochen Eisinger, Pavel Feldman, Mike West, Adam Barth
I see different results using Internet Explorer 11, 8 and Firefox...

Cross origin iFrame.contentWindow is always null (except in Internet Explorer, where it is never null).
Same origin iFrame.contentWindow is never null.

The load event does not change anything.

The test cases are attached.

Also, how is Window.document related? Perhaps you meant iFrame.contentDocument?


PhistucK

To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.

long-load-cross.html
long-load-frame.html
long-load.html

Yuki Shiino

unread,
Jan 28, 2015, 2:35:55 AM1/28/15
to PhistucK, Yuki Shiino, blink-dev, Kentaro Hara, Dmitry Gozman, Jochen Eisinger, Pavel Feldman, Mike West, Adam Barth
Thanks for many comments on this topic.  I figured out with you guys' help that iframe.contentWindow must be a valid WindowProxy object.  It seems we need another approach to fix the original issue.

By the way, the following code shows "null" on Firefox, but it's a little bit tricky code.  I see you guys' examples which are much more simpler show non-null.  Anyway, thanks for you guys' help taking me to the right way.

data:text/html,<script>function f() {document.removeChild(document.documentElement);iframe = document.createElement('iframe');iframe.src = "http://www.w3.org/";document.appendChild(iframe);alert(iframe.contentWindow);}</script><body onload="f()">

Indented version:
<script>
function f() {
  document.removeChild(document.documentElement);
  iframe = document.createElement('iframe');
  iframe.src = "http://www.w3.org/";
  document.appendChild(iframe);
  alert(iframe.contentWindow);
}
</script>
<body onload="f()">

If you guys are interested, any comments or help on the original issue are welcome.

Cheers,
Yuki Shiino

PhistucK

unread,
Jan 28, 2015, 3:05:59 AM1/28/15
to Yuki Shiino, blink-dev, Kentaro Hara, Dmitry Gozman, Jochen Eisinger, Pavel Feldman, Mike West, Adam Barth
What is the security risk here?
If it is a same origin iFrame, the parent page can do whatever it wants. Nothing risky here.
If it is a cross origin iFrame, the parent page is not supposed to be able to touch it (other than changing its location). If it can otherwise touch it, then this is a bug that should be fixed (set the origin of the window up front and disallow everything but changing the location as usual?).


PhistucK

Yuki Shiino

unread,
Jan 28, 2015, 3:19:40 AM1/28/15
to PhistucK, Yuki Shiino, blink-dev, Kentaro Hara, Dmitry Gozman, Jochen Eisinger, Pavel Feldman, Mike West, Adam Barth
I meant that the original issue is a security issue.  Sorry for confusion.

Boris Zbarsky

unread,
Jan 28, 2015, 11:08:47 AM1/28/15
to blink-dev
On 1/28/15 2:35 AM, Yuki Shiino wrote:
> By the way, the following code shows "null" on Firefox

Ah, this code is running into Firefox not creating browsing contexts for
an <iframe> or <frame> that has no parent element. See
<https://bugzilla.mozilla.org/show_bug.cgi?id=511084>.

-Boris
Reply all
Reply to author
Forward
0 new messages