Skip to content

Instantly share code, notes, and snippets.

@annevk annevk/coop.md Secret
Last active Feb 18, 2020

Embed
What would you like to do?

Cross-Origin-Opener-Policy response header (also known as COOP)

Introduction

This is a semi-formal definition of the Cross-Origin-Opener-Policy response header, to be merged into the HTML Standard pending various refactoring efforts. (See also the open issues.)

COOP can be used to prevent a site from opening a site with COOP in an auxiliary browsing context. The site attempting to do so will hold a reference to a closed browsing context and the site using COOP will be in a fresh top-level browsing context with its own browsing context group. A site using COOP can open auxiliary browsing contexts itself, if they are same-origin and also use COOP, or if it uses the same-origin-allow-popups COOP policy.

Goal

Integrate this into the HTML Standard.

Model

This should only work over a secure context.

To ensure robust isolation the processing model also needs to account for the Cross-Origin-Embedder-Policy response header (also known as COEP) to some extent. This document only defines how it augments COOP.

Document's have an associated cross-origin opener-policy, which is "same-origin + COEP", "same-origin", "same-origin-allow-popups", or "unsafe-none". It's initially "unsafe-none". (XXX: document creation needs to use "obtaining a cross-origin opener-policy".)

Note: "same-origin + COEP" is a special value that cannot be directly set, but is the result of setting COOP and COEP for a top-level document at the same time.

about:blank documents inherit cross-origin opener-policy from their creator's top-level browsing context's active document at the time of creation, if there is a creator and if the creator's origin is same origin with the creator's top-level origin.


To obtain a cross-origin opener-policy from a response response:

  1. Let value be the result of getting Cross-Origin-Opener-Policy from response's header list.
  2. If value is null, then return "unsafe-none".
  3. Let decodedValue be value, isomorphic decoded. (XXX: we should change Fetch so we can merge this into step 1.)
  4. If decodedValue is not "same-origin" or "same-origin-allow-popups", then return "unsafe-none".
  5. If decodedValue is "same-origin", then:
    1. Let coep be the result of obtaining a cross-origin embedder-policy from response. (XXX: the integration probably needs some slight adjustments as even if COOP ends up being "unsafe-none", we'd still want to respect COEP for non-COOP purposes. If we process COEP before COOP we could pass COEP into this algorithm instead.)
    2. If coep is "require-corp", then return "same-origin + COEP".
  6. Return decodedValue.

Note: the Cross-Origin-Opener-Policy header part is roughly equivalent to this non-normative ABNF:

Cross-Origin-Opener-Policy = %s"same-origin" / %s"same-origin-allow-popups" / %s"unsafe-none"; case-sensitive

To match cross-origin opener-policies, given A, originA, B, and originB:

  1. If A is "unsafe-none" and B is "unsafe-none", then return true.
  2. If A or B is "unsafe-none", then return false.
  3. If A is B and originA is same origin with originB, then return true.
  4. Return false.

Changes to navigation

Then, when navigating from a document doc in a top-level browsing context bc to a response response:

  1. Let currentCOOP be doc's cross-origin opener-policy.

  2. Let currentOrigin be doc's origin.

  3. Let potentialCOOP be the result of obtaining a cross-origin opener-policy from response.

  4. Let potentialOrigin be response's URL's origin. TODO: this should take CSP into account as that can make it an opaque origin (and future policies that can do similar things).

  5. If bc's popup sandboxing flag set is not empty and potentialCOOP is not "unsafe-none", then navigate bc to a network error and abort these steps.

    Note: This results in a network error as one cannot simultaneously provide a clean slate to a response using cross-origin opener policy and sandbox the result of navigating to that response.

  6. If the result of matching currentCOOP, currentOrigin, potentialCOOP, and potentialOrigin is false and one of the following is false

    • doc is the initial about:blank document
    • currentCOOP is "same-origin-allow-popups"
    • potentialCOOP is "unsafe-none"

    then:

    1. Let bcg be the result of creating a new browsing context group.
    2. If potentialCOOP is "same-origin + COEP", then set bcg's cross-origin isolated to true.
    3. Move loading of response to a new top-level browsing context newTLBC in bcg.
    4. If bc's popup sandboxing flag set is not empty, then:
      1. Assert: potentialCOOP is "unsafe-none".
      2. Set newTLBC's popup sandboxing flag set to bc's popup sandboxing flag set.
    5. Discard bc. [Note: this does not close bc's browsing context group, except if it was the sole top-level browsing context in which case it could be collected.]
  7. Otherwise, [do the normal thing].

Note: the intent is that this runs for each response, including redirects. User agents are expected to optimize away the many browsing context groups that would be created through a redirect chain, by keeping track as to whether it is needed at all.

Changes to choosing a browsing context

Then, a change is needed to "If the user agent has been configured such that in this instance it will create a new browsing context", which is part of the "The rules for choosing a browsing context" algorithm. In particular before the current algorithm runs:

  1. If current's top-level browsing context's active document's cross-origin opener-policy is "same-origin" or "same-origin + COEP", then:
    1. Let currentDoc be current's active document.
    2. If currentDoc's origin is not same origin with currentDoc's top-level origin, then set noopener to true.

Additional implications of "same-origin + COEP"

  • It impacts postMessage() and causes window.crossOriginIsolated to return true as detailed in HTML PR #4734.
  • As described in that same PR and proposed in issue #5122 it also impacts keying of agent clusters in the newly created browsing context (using origin rather than site as the key) as well as no-opping the document.domain setter. This still needs some compatibility analysis.
@jyasskin

This comment has been minimized.

Copy link

jyasskin commented Mar 20, 2019

Drive-by nit: "compare ..." should return some description of how the arguments relate, but the compare algorithm here just returns true or false. e.g. "Compare 3 and 4" could return "less" but not "true". Is there a more semantic description of what that operation is trying to do?

@annevk

This comment has been minimized.

Copy link
Owner Author

annevk commented Jul 2, 2019

I changed compare to match.

@ParisMeuleman

This comment has been minimized.

Copy link

ParisMeuleman commented Nov 8, 2019

Nit: In obtain a cross-origin opener-policy, should probably include a failure case similar to 7.iii for instances where the header is empty or only spaces.

Nit: In navigating from a document, I think 6.ii is redundant with 5.

In the matching, does "originA's host is same site with originB's host" mean same registrable domain as suggested in CORP: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy_(CORP)#Usage ?

thanks!

@annevk

This comment has been minimized.

Copy link
Owner Author

annevk commented Nov 10, 2019

Pâris, yeah, see same site in https://url.spec.whatwg.org/. The first nit will be clearer once we reference a bunch of algorithms for the operations (which should take of those cases) and the second doesn't apply since potentialCOOP is different.

@mikewest

This comment has been minimized.

Copy link

mikewest commented Nov 12, 2019

Spelling nit: If we modify the header syntax slightly, we can make it match the structured header syntax for items. That is, instead of two, space-separated tokens (Cross-Origin-Opener-Policy: same-site unsafe-allow-outgoing), we can have one token with a parameter (Cross-Origin-Opener-Policy: same-site; unsafe-allow-outgoing).

I don't think this makes a huge difference in practice, but if a one-character change allows us to align with The Future Of Parsing HTTP Headers™, it's probably worthwhile.

@annevk

This comment has been minimized.

Copy link
Owner Author

annevk commented Nov 12, 2019

@mikewest I strongly encourage you to review whatwg/html#4902 (comment) which would also enable that and improve some other things too.

@ParisMeuleman

This comment has been minimized.

Copy link

ParisMeuleman commented Nov 13, 2019

@annevk Thanks for the two links!
I didn't quite get the explanation regarding the second nit though. potentialCOOP changes between navigation steps 5 and 6.ii?

@annevk

This comment has been minimized.

Copy link
Owner Author

annevk commented Nov 14, 2019

Step 5 only applies when potentialCOOP is non-null. Step 6.ii only runs when potentialCOOP is null. Does that make sense? (I get the feeling I might be missing something from your reply, but cannot find it.)

@ParisMeuleman

This comment has been minimized.

Copy link

ParisMeuleman commented Nov 14, 2019

What I mean is that in 6.ii.a the assertion will never fail:

  • That is to say that PotentialCOOP cannot be non-null while bc's sandboxing flag set is non-empty in 6 since 5 aborts the process if that's the case.

Your last comment is reassuring though, as I feared I missed a pottential mutation there.

@annevk

This comment has been minimized.

Copy link
Owner Author

annevk commented Nov 14, 2019

Ah yes, specification asserts are meant to be correct: https://infra.spec.whatwg.org/#assert.

@ParisMeuleman

This comment has been minimized.

Copy link

ParisMeuleman commented Nov 14, 2019

Ah Thanks! sorry for the misunderstanding!

@hemeryar

This comment has been minimized.

Copy link

hemeryar commented Feb 18, 2020

Hey Anne,

I'm rereading the spec to make sure I implement the name changes correctly, but something seems weird:
Do we have any situation other than COOP where we would switch BrowsingContext "in tab"? Was BrowsingContext assumed to be uniquely set per frame and never change?

The reason I'm asking this is that we store things like SessionHistory in the BowsingContext? Did I miss the part where it gets copied under some circumstances or does that mean we also drop all ability to do back navigations?

@annevk

This comment has been minimized.

Copy link
Owner Author

annevk commented Feb 18, 2020

Reportedly Chrome switches the browsing context quite often (if there's only one top-level browsing context in a browsing context group)?

But yes, we need to introduce something like "browsing session" for representing a tab, holding the history, and holding the currently active browsing context. History hasn't really been tackled yet as what's written about it in HTML today is wrong: whatwg/html#1454.

Hope that helps.

@hemeryar

This comment has been minimized.

Copy link

hemeryar commented Feb 18, 2020

Makes sense! Indeed we switch BrowsingContexts quite often in Chrome (omnibox navigation for example) but the behavior is somewhat not well defined (window.name clears are not consistent, etc). Since the spec does not define these behaviors that's expected then I guess. I'll make a document that clarifies all the implementation assumptions.

@annevk

This comment has been minimized.

Copy link
Owner Author

annevk commented Feb 18, 2020

If you could find someone that could fix the specification for this that'd be much appreciated. (And yeah, we definitely need to define side effects such as window.name clearing normatively.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.