-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Restricting cross-origin WindowProxy access (Cross-Origin-Opener-Policy) #3740
Comments
/cc @whatwg/security. Very interesting... |
Note that there are several different proposals in this space last I checked. @mystor was working on one too, somewhat different from the one above. |
Note that this feature has been implemented in Safari 12: https://developer.apple.com/safari/whats-new/ |
I believe that in order to make this header allow all toplevel isolated documents to be put in a distinct process, we need to be more restrictive as to what we let same-origin but isolated documents do. Mini Window Agent ExplainerBefore I started talking to @annevk about this stuff, I wasn't familiar with Window Agents, so I figured I'd include a small explainer: Window Agents are the set of window globals which do, or may dynamically, have access to each-other's objects (other than the cross-origin Two globals in the same Agent Cluster must be loaded in the same process, even with complete Site Isolation. They may share objects, Isolated/Non-Isolated Interaction
In this case, under the current proposal, we cannot actually put the isolated B-1 into a separate process. The reason for this is that A could embed a non-isolated iframe B-2, which would be in B-1's Window Agent according to the current logic. This forces A to be same process with B-1, in case there exists a document B-2 which could be loaded, for example:
To fix this we need to prevent isolated and non-isolated documents from being in the same Window Agent, otherwise they can do the following: // In B-2
let b1 = window.parent.opener;
// b1 is same-origin-document with window, so we have to be same-process
b1.document // :'-( This brings us to the first restriction I think we have to make, namely:
Isolated/Isolated InteractionUnfortunately, that restriction is not sufficient. If For example, consider the case where a document B loads a frame C which, in turn, uses
In this situation, we cannot put A-1 in another process, as B could navigate the iframe C to be A-2, an isolated iframe which is
A-1 and A-2 must be in the same Window Agent, and thus the same process, as they are allowed to communicate, but we have no ability to move the iframe C out of process when loading A-2 in it, as we don't have OOP iframes. This brings us to the second restriction I think we have to make, namely:
Shared Array BufferWith those two restrictions, things are looking pretty good. Unfortunately, Unfortunately this breaks our isolation story again, for example:
In this case, B-1 and B-2 are Same-Site, so we have 2 Window Actors: Fortunately for us, A currently hides its opener from other documents due to its (mostly) opaque Cross-Origin Notably, if the Toplevel Browsing Context's document is not isolated, we don't have opener hidden due to
In this case, we can reach from This brings us to the last restriction I think we have to make, namely:
Restricted Window Agent Selection ProcessAll of that is a touch tricky to follow. This is the final, combined, Window Agent selection process: To select a global
ImplicationsThese are a few notable implications:
Allowed to NavigateAs navigating by named lookups can also create cross-global references (due to the opener property), we also have to restrict the allowed to navigate check, adding 2 new checks:
This should bring the checks in line with the Window Agent selection process. Other ChangesThis approach assumes that Window Agents are procedurally joined and used as the base of object access security. This is, however, not how the check is performed in the standard right now. We would want to change places which perform the Same Origin-Domain check for globals
This would change Window Agent selection to define access control, rather than being a result of it. Restricting
|
@mystor Thanks for the detailed feedback. All your points are valid and we made the same observation. However, what we concluded is that even those isolations are not enough because websites inside iframes would have access to the same set of cookies, local storage, etc... unless we treat it as a separate origin. If websites have access to those resources, we don't have a meaningful Spectre protection since the most sensitive information is stored in cookies and other storage APIs. For this reason, we believe that in order for websites to protect themselves against Spectre in a browser which only supports top-level process isolation MUST deny themselves loaded in an untrusted cross-origin iframe at all, and all of their resources must have More precisely, we believe websites MUST:
|
I'd also note that we've considered treating a website with |
My worry here is that I don't think we can provide process isolation even if Consider the example where we have a window
If I understand this proposal correctly, in this situation we definitely want For example,
As we don't support OOP iframes, the theoretical |
@mystor Thanks for the clarification. That's indeed an issue. There appears to be a number of possible solutions including but not limited to the one you proposed, checking all ancestor frames, clearing Meanwhile, we've disabled this feature in WebKit since this discussion is likely to result in an incompatible behavior change to the HTTP header we're proposing. Again, thanks for giving us the detailed feedback & engaging in the discussion with us. |
Just as an update, we're still discussing the best solution for this problem. Hoping to get back to you all within the next one month. |
@rniwa There's a typo, AFAICT in the first paragraph that tripped me up. You say "the value case-insensitively matches Deny ignoring cases" twice - I think the second one is supposed to be 'Allow-PostMessage', right? And another typo below: "Then b.com doesn't have access to a.com, and b.com doesn't have access to a.com so we can put them into two different processes." - the second b.com/a.com should be swapped I believe. |
@tomrittervg Thanks for pointing that out. Fixed. |
Okay, here's our feedback. Our new proposal is to only support While it's regrettable that web pages that require As I've previously stated, treating isolated and non-isolated pages as more or less different origins would make it harder to incrementally adopt this header across a website. We've analyzed the proposal made by @mystor in detail but we could resolve two issues:
We've also considered keeping |
Just wanting to make sure I have a clear idea of what your proposal here is, so I've done a slightly more technical writeup of what I am thinking you are suggesting. Please let me know if I am off-base. I think I like this idea, it's fairly simple, but is unfortunately destructive (breaks all WindowProxy references in perpetuity), and doesn't support PostMessage (though I have a potential ugly workaround at the bottom). While loading a DocumentThe following steps would be taken when loading a new document
Protections
Non-Protections
Side-Effects
Potential Variations
postMessage
This solution should be relatively straightforward to Firefox to implement, and it manages to avoid breaking all postMessage edges, but it probably can't be served on oauth popups.
I think I generally agree with this. I tried to think of how we could support postMessage within this framework, and came up with the following solution, which is unfortunately mildly compex/gross:
Effectively, this allows postMessage by keeping the method alive on closed WindowProxies created through the mechanism of this header, and using a dummy MessageEventSource method in the dispatched MessageEvent. |
Are you describing a same origin document or a cross-origin document? In the case of a cross-origin document, the proposal calls for zero access. In the case of a same-origin document, yes, the proposal allows access, but no protection is needed.
Yes, to ensure process isolation you must specify That said, the isolated document is still isolated in the sense that access to its window proxy properties is denied across origins.
A fresh auxiliary browsing context on the same origin will not be forced into a separate unit of related browsing contexts. But navigation to a different origin will trigger isolation. Isolation includes clearing the opener property. This allows a process swap and eliminates the need to specify
Right.
We don’t think this is mandated, but it is an option. We could go either way in the specification. Restoring the previous browsing context seems more web compatible.
Right.
We believe that |
I had a chat about an alternative model with @mystor. Ingredients:
When creating an auxiliary/nested browsing context:
When navigating:
Tradeoffs:
Goals:
UI:
|
Per the last F2F discussion we had, we'd like to keep the level 1 proposal / feature to be focused on providing a mechanism for websites to protect themselves from Spectre attacks. That is, providing a way to enable SAB would be a good addition to this feature but shouldn't be a part / requirement of it. Also see Artur's summary in the isolation-policy mailing list. Here's our latest proposal to that end. ProposalWe introduce a new HTTP header, let us call it Conceptually, this is as if the user had closed the tab/window in which the navigation was happening, and opened a new tab/window and navigated to the same page. There is no restriction on which website a document with the HTTP header can open in a new window or load in an iframe. DiscussionVarious discussions we've had on this issue and elsewhere made us realize that the key issue with the process swap on navigation (PSON) in browsers that don't support frame-level process isolation is To enable a website to protect itself, the user agent doesn't need to restrict the website’s ability to load cross-origin content in an iframe or a new window. In fact, that might be a necessary condition for some websites to adopt this new protection header. Imagine a banking website which opens another financial institution's website (e.g. credit card company's) in a new window in order to process a certain transaction. In such a case, it's probably okay for the websites to trust one another and be opened in a single process. In fact, such a flexibility might be a requirement for a website to incrementally adopt this new header. Making those observations, the only restriction we need for websites to protect themselves from Spectre attacks in a browser which doesn't support frame-level process isolation is that another website can't open it in an auxiliary browsing context. Note the website MUST prevent others from loading inside an iframe of a cross-origin site from the supposition. Furthermore, it seemed to us that the ability to protecting your own website from the opener browsing context from navigating, etc... seems like a useful feature regardless of Spectre. A Path to Level Two ProtectionTo re-iterate our position during F2F, we think it's valuable to provide a mechanism whereby browsers without a frame-level process isolation can re-enable One way to achieve the level 2 protection is to provide a way to prevent all descendent browsing context from loading a cross-origin content just like CSP's When the browser sees that a website has this second level protection set at the top-level browsing context, then it can enable One possible appraochWe would have two values for the header: Then the level two protection can be achieved by the combination of |
Thanks for writing that up. For "level 2" protection, is there any chance of including something like Mozilla's "X-Bikeshed-Allow-Unisolated-Embed" header (from the X-Bikeshed-Force-Isolate proposal) to allow web sites to opt in to being included in the same page and process as a site with SharedArrayBuffer access? Your description rules out all cross-origin browsing contexts, but that would (for example) prevent sites with SharedArrayBuffers from using ads, etc. I would imagine that ad sites might be ok with being embedded in such pages. |
@csreis : Perhaps. The level two protection proposal outlined in my latest comment is simply a possibility / an option. We're not necessarily fixated on it. Having said that, how can something like Again, our feedback / desire for the level two protection is that it ought to be something useful outside the context of Spectre and |
Thanks for writing this up @rniwa! I believe we will need to perform opener severing both for auxiliary and toplevel browsing contexts. If we only handle auxiliary browsing contexts, an attacker can open a new tab in the background, and then navigate itself to your toplevel page, attacking your document due to the I think a way to make |
A thing we have not discussed in detail here, though @mystor did mention it in #3740 (comment) is history. Firefox's current approach is history is copied somewhat into the new browsing context group, so What are Chrome and Safari considering here? |
For Chrome, we're keeping the session history intact across browsing context groups, both for the back/forward buttons and history.back() et al. Chrome manages the session history for a tab in the browser (parent) process, with a notion of browsing context group (BrowsingInstance) and process (SiteInstance) associated with each session history item. |
I think the desired behavior here is what @annevk described in #3740 (comment): we want history navigations via the browser UI to work, but ideally they wouldn't be accessible via A risk with allowing access to history from JS is that upon a navigation from a site with COOP to an attacker's document the browser may leak interesting information (e.g. an attacker can get information about the number of navigations on the victim site via If it adds significant implementation / spec difficulty then the severity of the leak is likely not worth addressing, but there are some benefits to removing JS access to history in this case. |
As a result of some related issues, in particular how inheritance ought to work and whether https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e is still the canonical document and I've updated it with these changes. |
Fixes whatwg#3740. Closes whatwg#4580. Need to check again if it closes them: * whatwg#4921 * whatwg#5168 * whatwg#5172 * whatwg#5198 (probably not?) Co-authored-by: Anne van Kesteren <annevk@annevk.nl>
Fixes whatwg#3740. Closes whatwg#4580. Need to check again if it closes them: * whatwg#4921 * whatwg#5168 * whatwg#5172 * whatwg#5198 (probably not?) Co-authored-by: Anne van Kesteren <annevk@annevk.nl>
Fixes whatwg#3740. Closes whatwg#4580. Need to check again if it closes them: * whatwg#4921 * whatwg#5168 * whatwg#5172 * whatwg#5198 (probably not?) Co-authored-by: Anne van Kesteren <annevk@annevk.nl>
Fixes whatwg#3740. Closes whatwg#4580. Need to check again if it closes them: * whatwg#4921 * whatwg#5168 * whatwg#5172 * whatwg#5198 (probably not?) Co-authored-by: Anne van Kesteren <annevk@annevk.nl>
This commit adds the notion of cross-origin opener policy (COOP). COOP allows websites to restrict which origins they share their browsing context group with. annevk wrote a first draft of the behavior of COOP here: https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e. This takes that draft and merges it into the spec, with many updates along the way. Closes whatwg#3740. Closes whatwg#4580. Closes whatwg#4921. Closes whatwg#5172. Co-authored-by: clamy <clamy@chromium.org> Co-authored-by: Anne van Kesteren <annevk@annevk.nl> Co-authored-by: Domenic Denicola <d@domenic.me>
The Cross-Origin-Opener-Policy header seems to be quite similar to what the rel="noopener noreferrer" attribute does when opening a new document in a new tab (target="_blank"). When should I use which one? It seems the COOP header is applicable when I link between origins while the rel="noopener noreferrer" attribute (on anchor tags) seems to work on the same origin as well? Should I use both? They seem to be quite complimentary.... Some advice here would be nice. |
Proposal
Add HTTP header called
Cross-Origin-Window-Policy
, which takes a value ofDeny
,Allow
, andAllow-PostMessage
.When the HTTP response of a document has a
Cross-Origin-Window-Policy
header, and the value case-insensitively matchesDeny
ignoring cases, the document is said to be fully isolated. If the value case-insensitively matchesAllow-PostMessage
ignoring cases, the document is said to be isolated with messaging. If the value doesn't match either or isn't set, then the document is said to be not isolated. If a document is fully isolated or *isolated with messaging", it is said to be isolated.In a fully isolated document, access to any property on the window proxy of a cross-origin document (regardless of whether the target document is fully isolated or not) results in a
SecurityError
. In a document isolated with messaging, access to any property exceptpostMessage
on the window proxy of a cross-origin document results in aSecurityError
. The restriction between two documents are symmetrical and the most stricter of the two prevails.Furthermore, a new step is inserted into the concept of allowed to navigate before step 1: If B and/or A is isolated and A and B are not of the same origin, return false.
Examples
Let document A and document B be distinct documents its own browsing contexts. If A and B are of the same origin, the header has no effect. If A and B are cross-origin, then:
If document B is fully isolated and document A is not isolated. Any attempt to access a property on document B's window from document A results in a
SecurityError. Any attempt to access a property on document A's window from document B also results in a
SecurityError`.If document B is isolated with messaging and document A is not isolated. Any attempt to access a property except
postMessage
on document B's window from document A results in aSecurityError
. Any attempt to access a property exceptpostMessage
on document A's window from document B results in aSecurityError
.If document B is isolated with messaging and document A is fully isolated. Any attempt to access a property on document B's window from document A results in a
SecurityError
. Any attempt to access a property on document A's window from document B results in aSecurityError
.If document B is isolated with messaging and document A is isolated with messaging. Any attempt to access a property except
postMessage
on document B's window from document A results in aSecurityError
. Any attempt to access a property exceptpostMessage
on document A's window from document B results in aSecurityError
.Spectre Protection Plan
For the purpose of protecting a website
a.com
from Spectre in browsers which support process swap for top-level navigations without frame-level process isolation,a.com
can set this header on all of its documents (not setting on some would result in leaks; more on this later).If this header is set on
a.com
, we can swap process on cross-origin navigation from or toa.com
's documents because this header guarantees thata.com
doesn't have access to any other document outside of its origin, and vice versa.Let's say we're on some page B1 in
b.com
, and it window.open'ed (isolated)a.com
. Thenb.com
doesn't have access toa.com
, andb.com
doesn't have access toa.com
so we can put them into two different processes. Obviously,a.com
's iframes don't have access tob.com
's frame tree either so if a website is currently relying on being able to do this, they won't be able to use this header.Let's say now
a.com
is navigated to some other page B2 inb.com
. In this case, the browser finds the process which loaded B1 and load B2 in the same process so that they can talk to one another via window proxies.The text was updated successfully, but these errors were encountered: