whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.17k stars 2.69k forks source link

Allow chrome-initiated toplevel navigations to isolate browsing context #2635

Open mystor opened 7 years ago

mystor commented 7 years ago

See https://github.com/whatwg/html/issues/1440#issuecomment-299275463.

The interesting case here is a toplevel navigation which is triggered by browser chrome, for example through typing a URL in the URL bar, performing a search, or loading a bookmark. In these cases, the new documents which are being loaded are conceptually separate from the previous navigations which occurred in the page, beyond existing within the same session history.

It would be nice to specify that when one of these navigations occurs, the browsing context can leave its unit of related browsing contexts, and become fully independent again, breaking any WindowProxy objects referring to it from windows which either opened it or were opened by it during the session history. This could be a win, both for security and for performance, as it gives user agents more control over process allocation, and can decrease the number of opportunities for cross origin webpages to tamper with one another.

As @annevk mentioned, this would probably require specifying new WindowProxy behavior for this situation, and determining what should occur during history navigations to documents which were loaded before this toplevel navigation.

This is related to #1515, in that exposing a tool like that would make it easier for link and webpage initiated navigations to opt into this more secure and performant behavior.

annevk commented 7 years ago

cc @estark37 @mikewest

mystor commented 7 years ago

There are a few different options for what this WindowProxy behavior could act like which I've thought a bit about:

The first option would be to make it such that one of these causes all auxiliary browsing contexts to permanently become disconnected from it, so the behavior will act the same as though the window which is being navigated was closed. This would have the advantage of not needing any new behavior which is not already implemented in browsers today, and I imagine could be implemented quite efficiently. This does however have the disadvantage that navigating back in history would not re-establish these connections which were broken.

The second option would be to annotate each session history entry with its URBC (or whatever procedural object @annevk would specify to replace this concept). Each history navigation could cause the window to move between URBCs. Whenever a window name needs to be looked up it would occur in the URBC for the current history entry, and all WindowProxy (and Location, etc.) objects would act as though the target window was closed, unless the target browsing context was currently in the same URBC. Then, we would simply (ha!) make the chrome initiated navigation create a new URBC and move the window into it.

If we're already planning to procedurally specify URBC in the way that @annevk was suggesting, this may be a fairly straightforward way to specify the behavior, although it will likely be more complex for user agents to implement.

mikewest commented 7 years ago

Between the two, I prefer the first option (some extension of "disown the opener" that breaks the link between the window and any references to its WindowProxy, perhaps along the lines of the discussion in the thread at https://lists.w3.org/Archives/Public/public-webappsec/2017Apr/0071.html). I agree that it would be a little annoying for history navigation, but I worry about making WindowProxy security checks too dynamic/expensive. I also think it's just simpler conceptually to consider a window that a user has taken control over as "the user's window" rather than "the developer's window".

mystor commented 7 years ago

@mikewest I agree that the first option is definitely the most elegant, at least spec-wise, and I also definitely don't want to make those WindowProxy security checks too expensive. That being said I'm not sure how much more expensive the second will be than the first to implement (at least with my understanding of Gecko's architecture. I'm not well aware of Chromium's etc.).

I'm imagining that for the second option, we would have an identifier which represents the current "Unit of Related Browsing Contexts" (URBC, in Gecko we call this a TabGroup) which is associated with each window object. When running script for a given page, we stash its URBC somewhere, and when performing window proxy property loads, we compare the URBC of the WindowProxy with the URBC of the currently running script. We would then also add a URBC to each session history entry, and just swap these properties out as we perform history loads. (We would also want to allow process swaps if two pages aren't in the same URBC, but that seems very implementation specific).

For the first option I imagine we could migrate the browsing context into a brand new object, breaking all existing references (as they would refer to a now-dead object), but I'm not convinced it would be significantly more performant than using URBC identifiers, so we might implement it that way for flexibility in the future anyway.

I also like the approach of making accesses to these properties raise a SecurityError for either of the two options.

annevk commented 5 years ago

This will be very simple to add once #3740 is a thing and I think we should make it a "should" requirement at that point.

annevk commented 3 years ago

Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1611183.