The problem
While working on a COOP deployment strategy across our apps we came across a potential issue which might be common enough to warrant supporting at the spec level. The situation is as follows:
Cross-Origin-Opener-Policy: same-origin for all of its responses. It seems useful for developers to be able to exempt parts of their site from COOP enforcement to enable incremental rollouts.
A possible solution
I believe we could solve this by adding a new value of unsafe-inherit to COOP. When a document sets Cross-Origin-Opener-Policy: unsafe-inherit it would elect to be put into the browsing context group of the opening document, assuming the opening document's own COOP doesn't prevent this.
That is:
COOP: unsafe-inherit then the popup would end up in its browsing context group and the documents can communicate.COOP: unsafe-inherit, then:same-origin / same-site, the documents would be in the same browsing context group.unsafe-allow-outgoing.This would allow sites like OAuth providers set COOP: unsafe-inherit on documents which need to be accessible by cross-origin windows while using a stricter COOP elsewhere in the same origin/site.
Similarly to CORP this could also provide a path to setting COOP restrictions by default sometime in the future by allowing developers with resources which need to be accessed cross-origin with a way to opt out of the restrictions.
Note: The original text of this issue referred to this mode as cross-origin instead of unsafe-inherit and references to cross-origin still remain in some of the discussion below.
@mikewest @annevk @empijei @csreis @lweichselbaum
Overall this makes sense, but I wonder if this would require other nuanced changes. E.g., currently same-site in one COOP and same-origin in another constitute a non-match (irrespective of their hosts). This would put more strain on where the COOP comes from as it's now quite important which you use for comparison purposes. In turn, this would complicate testing further as well.
Thinking about this some more, I wonder if instead we can make this a different keyword from cross-origin: unsafe-inherit or some such. This keyword signifies that you take over the COOP of whoever initially created the browsing context group you are being pulled into. I think this would avoid all the model changes a keyword such as cross-origin implies.
Minor nit: since we already have unsafe-allow-outgoing, why not call this unsafe-allow-incoming?
This would, in fact, allow openees to be opened in someone else's context, which is the reciprocal action of allowing yourself to bring someone else in your context (u-a-o).
I think the one problem with that is that this is not only unsafe-allow-incoming, since outgoing is inherited as well, right? If A sets same-origin unsafe-allow-outgoing and loads B which sets unsafe-allow-incoming, B gets to open whatever.
One issue @mystor raised that is good to keep in mind is that this proposal requires remembering the final computed state in session history. That is, in
example.com _A_ with policy same-origin popups example.com _B_ with policy unsafe-inherit.same-origin.elsewhere.example with policy does not matter.same-origin.it would be bad if 5 was not true I think.
unsafe-inherit or something like that sounds quite reasonable to me, as does the requirement to restore the computed state of COOP upon B/F navigations.聽
Thinking about this even more, I wonder if we could make 'unsafe-inherit' the default mode, i.e. have the lack of a COOP header imply the behavior we're talking about here (keeping the browsing context for same-origin / same-site navigations depending on the COOP value of the opener page).
A negative consequence of this model is that there would be "action at a distance", i.e. a page could exhibit different behavior depending on whether the user arrived on it from a page with COOP or not. Requiring developers to explicitly set unsafe-inherit could also be more auditable and allow developers to find all documents in their apps that opt themselves into this mode.
The upside is that this seems easier to deploy -- not setting COOP on some documents would still result in expected behavior, i.e. same-origin navigations/popups which open that document would retain the existing browsing context group, rather than creating a new one. It also seems to not poke holes in the security model because all navigations outside of the scope defined in the original COOP (same-origin or same-site) would result in a new browsing group. The one exception is if the original page sets COOP with unsafe-allow-outgoing, which would now make cross-site links from same-origin-no-COOP pages open in the same browsing context, but this seems like a minor issue (and we could make unsafe-allow-outgoing not inherit if need be).
Do you have any thoughts about this @annevk?
I would like to understand your final example better:
same-origin unsafe-allow-outgoing.same-origin unsafe-allow-outgoing.How would this not replace the popup browsing context?
In your example, if we open a second popup from the document with the inherited COOP, then that popup will be in the same browsing context group because of unsafe-allow-outgoing, right?
So, previously, if some parts of your origin set COOP with unsafe-allow-outgoing, the of risk putting a cross-origin document in the same browsing context group would be limited to only documents which explicitly set the header. In the inherit-by-default world that risk expands to same-origin documents which don't set COOP, because their popups will remain in their context group.
I have to say that I'm skeptical about this being a meaningful security risk, because it applies only in cases where an application uses unsafe-allow-outgoing, doesn't set COOP origin-wide, and allows COOP-less documents to open cross-site popups. Even then, an attacker would have to convince the user to have a series of interactions with the site that would result in opening a popup to an attacker-controlled site. If this overall model sounds reasonable, then my guess is that a spec note about this concern could be sufficient...
It will be in the same browsing context group, but that would also happen without inheritance (because then the unsafe popup would lack a policy). So I don't think that's a novel angle? Also, if a victim resource does not set COOP, an attacker could also open it into a popup or (unless protected) a frame (see also XSLeaks).
You're right, the concern I described for unsafe-allow-outgoing is not any different from the status quo; I forgot that unsafe-allow-outgoing already assumes trust in navigations from your popups.
I think there is still an issue with inheritance, but it's different:
same-origin and open a same-origin popup without COOP.top.openerPreviously, the popup would be in a different browsing context group because of a same-origin navigation to a no-COOP document. With inheritance, the same-origin popup will be put in the same browsing context group and its frame will get access to the opener.
It's hard to say how realistic this is (the scenario seems somewhat contrived), so the main question is whether the adoption benefits would outweigh this concern.
I think I agree that implicit copying of the policy is probably better. We can add another value (e.g., strict) in the future that'd avoid that kind of thing, if desired. It's potentially weird that with this model same-origin and same-site are a non-match, but probably okay? (I could see that if you pull same-site into same-origin it becomes same-origin, but not sure how great that is and it still leaves all the other cases as non-matches.)
What we did not explicitly consider is that inheriting by default might give complications if a document is unsure about its "outgoing" links. That is, if A1 adopts COOP and A2 does not and A2 needs to load B in a popup and A1 sometimes loads A2 in a popup, it depends on whether or not A1 specifies unsafe-allow-outgoing whether A2 is going to function.
So therefore, explicit inheritance might be better:
same-origin / same-site; sure about incoming and outgoing linksunsafe-allow-outgoing; sure about incoming, unsure about outgoing linksunsafe-inherit; unsure about incoming, sure about outgoingA variant not in the above table is the combination of unsafe-inherit and unsafe-allow-outgoing. It's not clear to me there's any benefit to that, but you might end up with that if whoever opened you had unsafe-allow-outgoing set (or lacked COOP).
The COOP processing model has support for unsafe-inherit now and the interaction with COEP is defined as well.
Back in https://github.com/whatwg/html/issues/4581#issuecomment-496522259 @annevk mentions a conversaion with me which relates to session history. I think my mind might have changed on what behaviour we should take there. I originally thought we needed to store the inherited COOP state inside of session history, so that when going back in history it would act the same. This would be needed to make the behaviour when the document is in the BFCache match the behaviour when it is not better.
I'm not convinced that this is the right decision, given that the behaviour of loading something out of history is already poorly defined, and can be quite different depending on what has happened, including history being truncated, surviving across browser restarts, and being evicted from the cache. It seems like it might be much simpler to not store this information, and consider loads from history to, if the document isn't bfcached, be effectively a new fetch from the network, inheriting from the new context it is loading in.
I'm not super attached to this, but I'm not sure that storing this extra information in session history is worthwhile, especially considering how underspecified history is right now. :woman_shrugging:
Given that this is already in the test suite and part of the processing model I'm going to close this, but see #5044 for some follow-up work regarding this feature.