[whatwg] postMessage: event.source allows navigation of sender

On Thu, 7 Feb 2008, Hallvord R M Steen wrote:
>
> Adam Barth and Collin Jackson pointed out to me that while investigating 
> frame navigation policies they found that a recipient of a postMessage 
> in Opera can set event.source.location, thus navigate the sender 
> window/document. I think this is a bug in the API itself.
> 
> This seems to violate the API's promise of safe cross-domain 
> communication even with untrusted documents. One can imagine use cases 
> where a script in document A has a reference to window B and thus can 
> post messages, but window B does not have any to A and would not under 
> normal circumstances be able to change A's address.
> 
> I think this should be adressed by removing event.source entirely. It 
> would be weird to disallow setting location on a window object in this 
> context only. To allow posting replies we could instead define a 
> function on the event object. Say for example
> 
> document.addEventListener(  'message', function(e){
>     if(e.data=='Hi'){
>         e.reply('Hello');
>     }
> }, false  )

As far as I know there are no non-symmetric Window visibility cases in 
HTML5. You can only postMessage() to a Window if the Window can see you. 
Is that not true?

Anyway, getting a hold of a Window and setting its window.location.href 
are two different things, as noted below.

The idea is that message channels are the solution around this, by the 
way -- you sent a port to a window you have access to (and that has 
access to you) and if it passes it on to a window you _don't_ have access 
to, it can't navigate you.


On Thu, 7 Feb 2008, Thomas Broyer wrote:
>
> Shouldn't event.source.location be read-only? Isn't that a direct 
> application of the same-origin policy?

Yes and no -- location.href only allows navigation if a separate access 
check passes (not the same-origin check).


Adam explains it well:

On Thu, 7 Feb 2008, Adam Barth wrote:
> 
> When one frame posts a message to another frame, the recipient frame 
> obtains a pointer to the sender frame as the "source" attribute of the 
> message event.  In Opera, this leaks the capability to navigate the 
> sender's frame to the recipient because Opera assumes that if a script 
> has a JavaScript pointer to a frame then that script is permitted to 
> navigate that frame.
> 
> The source attribute of the message event does not leak any privileges 
> to the recipient in Internet Explorer, Firefox, and Safari because these 
> browsers do not make this assumption and instead check whether the 
> script is permitted to navigate the frame when the script assigns 
> window.location.
> 
> In Opera, it is difficult to obtain a JavaScript pointer to a frame 
> because Opera prevents scripts from reading window.frames[i] across 
> domains.  Internet Explorer, Firefox, and Safari all allow scripts to 
> read window.frames[i] across domains.
>
> [...]
> 
> Another way to resolve the issue is for Opera to match the other
> browsers and check whether a script is permitted to navigate a frame
> when that scripts assigns the frame's location.

Right. This is defined here:

   http://www.whatwg.org/specs/web-apps/current-work/#security6
   http://www.whatwg.org/specs/web-apps/current-work/#allowed



On Thu, 7 Feb 2008, Hallvord R M Steen wrote:
> 
> Implementing the ancestore policy takes care of most of the scenarios I 
> can think of where you may want to post messages to a window that should 
> not be allowed to change your location. One case I'm still somewhat 
> concerned about is that one is allowed to set the location of any 
> top-level window according to the ancestor policy, so calling 
> postMessage on untrusted windows from your top window is still somewhat 
> dangerous. That's something we have to allow for web compatibility and 
> for this reason I still think removing event.source from the message 
> event interface would be a good idea.

We can't remove window.source, that's how you talk back. :-)


> For example, consider
> 
> w=window.open();
> w.opener = null;
> w.location = 'http://untrusted.example.org'
> w.postMessage( '...' );
> 
> Untrusted content now gets a window reference it would not otherwise 
> have, and will be allowed to set location if this scripts runs in the 
> top context of the opener.

If the top-level location changes, the location bar will change too, and 
after that the pages can't communication usefully (all you can do is a 
phishing attack, but then the evil page could just as easily just open a 
new window and hope the user doesn't realise there are two windows open, 
it would have the same effect -- or just redirect its own window to the 
phishing window, or whatever).

If this is really a concern, then use the <iframe sandbox> option. The 
sandboxed navigation browsing context flag will then take care of 
preventing any undue navigation.


On Sat, 9 Feb 2008, Adam Barth wrote:
> 
> One possibility is to prevent one frame from navigating another if the 
> frames are in different units of related browsing contexts.  This is 
> consistent with HTML5's philosophy that different units of related 
> browsing contexts could be run in different threads and do not interact 
> directly.  That way, if a user visits a web site by first creating a new 
> tab, the existing frames cannot navigate the new site's frame.

This is already the case, since there's no way to get a hold of a Window 
object from another unit.


> In addition, we could define a new target, for example "_unrelated", 
> such that calling
> 
> window.open(url, "_unrelated")
> 
> would open the URL in a new unit of related browsing contexts.

<a href="..." rel="noreferrer"> basically does this per spec today.

I don't want to do it for window.open() because window.open() in legacy 
UAs always returns a Window object, which would defeat the point.


On Sat, 9 Feb 2008, Jeff Walden wrote:
>
> While to an extent it seems there's agreement that the ancestor policy 
> works well enough to not worry overly much about postMessage exposing 
> the sender's window, the top-level window thing is a pretty big hole 
> that breaks dumping content in an iframe if that content can't be 
> trusted.  postMessage exposing the sender isn't that meaningful if you 
> can just change the toplevel site, even if the location bar changes 
> correspondingly.  (Really, who looks at the location bar on even 
> dynamic, JSy pages other than after an intentional navigation?  I doubt 
> I do.)

The point is that you can't distinguish changing a top-level frame from 
just opening a brand new top-level frame, so it's not really gaining you 
anything. The "sandbox" feature of <iframe> in HTML5 deals with this.


> That said, I think a reply() method might be a better idea than a source 
> property.  I'm not sure how meaningful that would be for MessageEvent 
> generically, but I guess source had that problem already for server-sent 
> events.  Do note it's more spec burden due to determining sender 
> identity (could be mitigated by equating reply with calling postMessage 
> on the proposed-hidden source property); dynamic scope-dependent 
> behavior is no fun that way.

I'm a little concerned about adding methods to the Event object, there 
isn't any precedent for that really. I'm also not convinced the attack 
scenario is really a problem.

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

Received on Tuesday, 29 July 2008 15:48:29 UTC