whatwg / html

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

COEP and COOP inheritance for top level non-HTTP documents #6552

Open ParisMeuleman opened 3 years ago

ParisMeuleman commented 3 years ago

When creating a new browsing contexts, COOP is inherited from the opener's top level document, and COEP from the opener. In https://github.com/whatwg/html/issues/5198, we also clarify COOP inheritance for non-HTTP documents beyond the initial empty document. COEP inheritance for this case is not clear to me.

This inheritance seems to be problematic in the following case:

According to the rules above, C's initial empty document should have COOP: same-origin and COEP: require-corp. This is problematic since an HTTP response with COOP: same-origin and COEP: require-corp would lead to a COOP value of same-origin-plus-coep. I would suggest we adopt one of the following inheritance strategies for the initial empty document

  1. Align COEP with COOP, i.e. if COOP is same-origin we force COEP to unsafe-none. It is unclear however what we should do to COEP for COOP same-origin-allow-popups and COOP unsafe-none.
  2. Apply COOP inheritance for top frames to COEP.
  3. Open the popup noopener, no COOP/COEP will be applied. the trigger will probably be if opener’s COOP == same-origin and opener’s COEP == require-corp.
  4. Keep COOP same-origin and COEP require-corp; i.e. we accept the discrepancy in the COOP value.
  5. Update COOP to same-origin-plus-coep and open the popup in a different browsing context group. I'm not sure this is feasible (beyond using noopener).

Regarding D case, D's document (loaded from a local scheme url) should have COOP: same-origin, but COEP is unclear to me. I suggest we apply a COEP inheritance here that matches what we decide for the initial empty document.

  1. Align COEP with COOP.
  2. Apply COOP inheritance to COEP (for top).
  3. Align COOP with COEP, i.e. update COOP to same-origin-plus-coep, this should trigger a browsing context group switch.
  4. Keep COOP same-origin and COEP require-corp, accepting the discrepancy.
  5. Keep the previous document COEP.

I am not sure what we prefer here. 4 in both scenarios seems acceptable as I think it would minimize the risk of breakages during deployment of COEP to the embedded frames. I'm just a bit worried

ghost commented 3 years ago

Maybe related in WICG/webpackage: Define how CORP and COEP should work for subresource loading (#644)

ParisMeuleman commented 3 years ago

@arturjanc @annevk I guess you'll have opinions on this.

@camillelamy @mikewest @ArthurSonzogni @antosart FYI

annevk commented 3 years ago

I recall that the idea was that COEP would only ever inherit downward and not into popups (except as part of COOP's special same-origin-plus-coep). But maybe that's only specified as far as enforcing COEP goes.

I'm not sure how reasonable that is for local schemes, but let's consider it. For blob: URLs we'd probably do want to inherit via the blob URL store. Though if B created it COOP would end up as unsafe-none I suspect? For (initial and non-initial) about:blank that seems fine. Yes, you can circumvent COEP, but that's always possible without top-level COOP involvement. For data: URLs, meh? Anything else?

ghost commented 3 years ago

I am not sure what we prefer here.

Whatever we prefer here, the inheritance can only, at best, transmit a "Potentially Trustworthy" environment but not a truly secure environment. If the risk of breakages is what we are primarily concerned about, then it might be best to serve important resources in same origin context.

annevk commented 3 years ago

It's secure (or at least as secure as we know how to make it), whether it's trustworthy very much depends on your threat model and the relevant party. (It's not a great name, but this isn't the place to discuss that.)

ParisMeuleman commented 3 years ago

I recall that the idea was that COEP would only ever inherit downward and not into popups (except as part of COOP's special same-origin-plus-coep).

IIUC you suggest that COEP is deduced from COOP special same-origin-plus-coep and defaulted for other COOP values, for relevant local schemes?

For blob: URLs we'd probably do want to inherit via the blob URL store. Though if B created it COOP would end up as unsafe-none I suspect?

Interesting, I'd have expected a similar inheritance for the blob than for the about:blank i.e. from the initiatorcreator's top level, if they are same-origin, defaulted otherwise.

For (initial and non-initial) about:blank that seems fine. Yes, you can circumvent COEP, but that's always possible without top-level COOP involvement.

I'm not sure I get the conclusion. Do you mean it is fine that about:blank inherits COOP and COEP (same-origin and require-corp) from their initiator/opener and their top? without considering altering COOP nor COEP. Or do you mean we don't inherit COEP hence the resulting document would be COOP: same-origin, COEP: unsafe-none?

For data: URLs, meh? Anything else?

👍

annevk commented 3 years ago

The idea (not sure how great it is still) is to inherit COEP downward (i.e., nested documents), but not upward/sideward (i.e., top-level documents) for relevant local schemes.

I could see inheriting COOP for blob: URLs as well. That makes sense. They're a bit special as you don't know if they are going to be nested or top-level documents.

I mean that we don't inherit COEP for about:blank (i.e., results in unsafe-none) and that it's fine.

ParisMeuleman commented 3 years ago

If I understand you correctly, you're saying it is fine if we have a top level about:blank with COOP same-origin-plus-coep and COEP unsafe-none? I thought this document can be used by the opener (for instance) to execute/embed things.

Note that Chromium inherits here, I think Firefox does too, for instance:

main frame on https://first-party-test.glitch.me/?coep=require-corp&coop=same-origin& execute :

a = open();
ifr = a.document.createElement("iframe");
ifr.src = "https://www.example.net";
a.document.body.appendChild(ifr);

The iframe won't load. Without COEP the iframe will load.

COOP for blob: not sure how we could do that spec wise, but I'd see something like when creating the blob we determine the COOP to be used, when navigating towards it we either use it or ignore it if it is nested (as we do for http responses actually)

annevk commented 3 years ago

No, if COOP is same-origin-plus-coep COEP should be require-corp. You do get inheritance in that case (but it's "through COOP").

ParisMeuleman commented 3 years ago

Ok thanks for clarifying!

In my example, Chromium's behavior is wrong then (As it inherits even without COOP), I'm not sure how to interpret Firefox's, the iframe is blocked but I'm not sure due to what.

ArthurSonzogni commented 3 years ago

If you try to deduce COEP from COOP, this will be difficult:


There are a few properties I think we want to preserve:

  1. If a top-level document has COOP == same-origin-plus-coep, then COEP != unsafe-none.
  2. When creating the initial empty document in the same browsing context group (e.g. without noopener), it inherits its capabilities to fetch from its creator. Doing something differently would be hard to implement, given this isn't part of the navigation. As a result, the initial empty document COEP must match its creator, because they have the same capability to fetch.

I realize I am only trying to find problem in previous solutions, but I am not trying to work on a solution myself. So I feel obligated to propose something. Not sure how great it will be.

The fact that COOP is somewhat tied to COEP makes things hard. I believe blocking things is easier than trying to imagine a working inheritance mechanism.

Every new document are created by a navigation, except the initial empty document. So we probably should distinguish the two:

  1. Initial empty document for popup. Follow the current specification. When the popup is same-origin with the top-level opener, COOP is inherited from the top-level opener. COEP is inherited from the direct opener. If COOP == same_origin &&COEP != unsafe-none. Then open the popup with noopener and do not inherit anything. Alternatively you can also not open the popup.
  2. Initial empty document for iframe. Follow the current specification. Nothing really special.
  3. Navigation. Follow the current specification. Define inheritance the way you want. Maybe via PolicyContainer. No matter what happen, if you ends with COOP:same-origin and COEP != unsafe-none, I would say it is easier to cancel the navigation or commit an error document instead.
camillelamy commented 3 years ago

I would like to avoid introducing a new case of opening a popup with rel no-opener if we can avoid it. For the case 1, I'd rather we follow COOP inheritance: i.e. inherit the top-level COEP of your page if you are same-origin with the top-level document.

ArthurSonzogni commented 3 years ago

I would like to avoid introducing a new case of opening a popup with rel no-opener if we can avoid it. For the case 1, I'd rather we follow COOP inheritance: i.e. inherit the top-level COEP of your page if you are same-origin with the top-level document.

I would be happy not to, if there are better alternatives.

So, for the initial empty document in popup, you propose inheriting COEP from the opener's top-level document instead of the current behavior: inheriting from the opener. This seems problematic, without noopener, the capability to fetch (e.g. URLLoaderFactory) are currently inherited from the opener. The capability to fetch is already bound with a COEP/COEP-Reporting policy. To be consistent, if you decide to inherit COEP from somewhere else, you also need to inherit the capability to fetch from the same place. See point n°2 from my previous message in [There are a few properties I think I want to preserve]. This would be a big change. I won't be able to foresee all the consequences of this. WDYT?

annevk commented 3 years ago

How about we inherit COEP from the creator if we determine that the COOP value is same-origin-plus-coep and otherwise we do not inherit COEP at all?

ghost commented 3 years ago

I just unsubscribed from all this madness. Sorry for the disturbance.

ArthurSonzogni commented 3 years ago

How about we inherit COEP from the creator if we determine that the COOP value is same-origin-plus-coep and otherwise we do not inherit COEP at all?

Is this the same as what I proposed? I believe the single difference is: In your proposition, you don't add "noopener". Is this what you meant?

The difficulty I have with not adding "noopener" is that in this case, the initial empty document inherits its capability to fetch from the creator, and this capability is already bound to a COEP policy. When setting the initial empty document's COEP, we aren't really enforcing anything new, we are just reflecting what is already in use. What I describe is probably Chrome specific though. Going into this direction might be possible in theory, but seems difficult to me at the moment for Chrome (+ @yutakahirano FYI )

annevk commented 3 years ago

Yeah, I think so. I think it would be rather strange to force noopener because of creator's COEP.

Unless COEP is applied together with COOP, there is essentially no consistency as you could typically find another document to fetch from (at least in these same-origin scenarios).

ArthurSonzogni commented 3 years ago

Yeah, I think so. I think it would be rather strange to force noopener because of creator's COEP.

Yes, indeed. I agree you with you. What I proposed isn't great.


I am looking into (4) from @ParisMeuleman proposition. If we don't want "noopener", and can't inherit COEP outside of the creator for the initial empty document, then this might be a good solution.

ParisMeuleman commented 3 years ago

regarding 4), while it seems like a reasonable option to me, I'm not sure to understand what [should] happens in the following cases:

ArthurSonzogni commented 3 years ago

regarding 4), while it seems like a reasonable option to me, I'm not sure to understand what [should] happens in the following cases:

  • Document with COOP: same-origin, COEP: require-corp navigates to document with (network) COOP: same-origin, COEP: require-corp => Probably a browsing context group switch, COOP of the new document is same-origin-plus-coep.
  • Document with COOP: same-origin, COEP: require-corp navigates to document with COOP: same-origin, COEP: none => no switch?

=> I would assume yes to both. The current specified logic about browsing context group swap applies.

  • (unrelated to the option 4) itself, but related with the inheritance) Document with COOP: same-origin-plus-coep navigates to document with (network) COOP: same-origin and COEP: cors-or-credentialless. I guess this boils down to : what's the COOP resulting of a response with COOP: same-origin, COEP: cors-or-credentialless. You seem to imply Arthur that it should be same-origin-plus-coep, Is that correct? Is it really ok to have both require-corp and cors-or-credentialless in the same browsing context group? My understanding is it could be, given that cors-or-credentialless can include iframes with require-corp.

=> Yes: same-origin-plus-coep. Tentative HTML spec here: https://whatpr.org/html/6638/origin.html#the-headers