Skip to content
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

Coop noopener allow popups #36232

Merged
merged 22 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
954cc93
Add COOP noopener-allow-popups values
yoavweiss Oct 7, 2024
6978003
Update files/en-us/web/http/headers/cross-origin-opener-policy/index.md
yoavweiss Oct 8, 2024
028adf8
style
yoavweiss Oct 11, 2024
ef53be3
WorkerGlobalScope.crossOriginIsolated - tidy
hamishwillee Oct 14, 2024
d5853fc
Window.crossOriginIsolated - tidy
hamishwillee Oct 14, 2024
e729d62
Apply suggestions from code review
hamishwillee Oct 18, 2024
b04a103
Improve crossOriginIsolated property doc
hamishwillee Oct 18, 2024
6d3c5ae
Update glossary: Browsing context to also mention the group and cross…
hamishwillee Oct 18, 2024
423a735
Update index.md
hamishwillee Oct 18, 2024
1973324
Commit suggested change to intro
hamishwillee Oct 21, 2024
5270c07
Update directives
hamishwillee Oct 21, 2024
5c13a19
Try explain the purpose of each directive succintly
hamishwillee Oct 21, 2024
21d2264
Tidy example a little
hamishwillee Oct 21, 2024
90d6b77
minor tweaks
hamishwillee Oct 25, 2024
1e1cb85
Window.open() add note to return value
hamishwillee Oct 25, 2024
cffa294
Update files/en-us/web/http/headers/cross-origin-opener-policy/index.md
hamishwillee Oct 25, 2024
fee2efc
Use term window.open() by preference to popups
hamishwillee Oct 29, 2024
9429853
same-origin-allowpopups reason
hamishwillee Oct 29, 2024
426836e
Window.open() returns object with Window.closed for coop in other BCG
hamishwillee Nov 5, 2024
3b82c9e
Window.closed not null in coop doc
hamishwillee Nov 5, 2024
9692444
Fixes and addition of tables
hamishwillee Nov 11, 2024
596a115
Update files/en-us/web/http/headers/cross-origin-opener-policy/index.md
hamishwillee Nov 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions files/en-us/glossary/browsing_context/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@ page-type: glossary-definition

{{GlossarySidebar}}

A **browsing context** is an environment in which a browser displays a {{domxref("Document")}}. In modern browsers, it usually is a _tab_, but can be a _window_ or even only parts of a page, like a _frame_ or an _iframe_.
A **browsing context** is an environment in which a browser displays a {{domxref("Document")}}.
In modern browsers, it usually is a _tab_, but it can be a _window_, a _popup_, a [web application](/en-US/docs/Web/Progressive_web_apps), or even a part of a page such as a _frame_ or an _iframe_.

Each browsing context has an origin (that of the active document) and an ordered history of previously displayed documents.
Communication and resource sharing between browsing contexts is constrained, in particular between cross-origin contexts.
For example, a {{domxref("BroadcastChannel")}} can only be opened and used to communicate between same origin-contexts.

Communication between browsing contexts is severely constrained. Between browsing contexts of the same origin, a {{domxref("BroadcastChannel")}} can be opened and used.
A browsing context may be part of a **browsing context group**, which is a set of **browsing contexts** that share common context like history, cookies, storage mechanisms and so on.
The browsing contexts within a group retain references to each other and can therefore inspect each other's global objects and post each other messages.

By default a document opened from a browser context group is opened in the same group whether or not it is cross-origin or same-origin.
The {{httpheader("Cross-Origin-Opener-Policy")}} can be used to control whether the document is instead opened in its own new browsing context group and {{domxref("Window.crossOriginIsolated","cross-origin isolated","","no code")}} from other contexts (in particular cross-origin contexts).
The can mitigate the risk of cross-origin attacks and the side-channel attacks referred to as [XS-Leaks](https://xsleaks.dev/).

## See also

Expand Down
15 changes: 12 additions & 3 deletions files/en-us/web/api/window/crossoriginisolated/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,23 @@ browser-compat: api.crossOriginIsolated

{{APIRef("DOM")}}

The **`crossOriginIsolated`** read-only property of the {{domxref("Window")}} interface returns a boolean value that
indicates whether the website is in a cross-origin isolation state. That state mitigates the risk of side-channel attacks and unlocks a few capabilities:
The **`crossOriginIsolated`** read-only property of the {{domxref("Window")}} interface returns a boolean value that indicates whether the document is cross-origin isolated.

A cross-origin isolated document only shares its {{glossary("Browsing context","browsing context group")}} with same-origin documents in popups and navigations, and resources (both same-origin and cross-origin) that the document has opted into using via [CORS](/en-US/docs/Web/HTTP/CORS) (and [COEP](/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) for `<iframe>`).
The relationship between a cross-origin opener of the document or any cross-origin popups that it opens are severed.
The document may also be hosted in a separate OS process alongside other documents with which it can communicate by operating on shared memory.
This mitigates the risk of side-channel attacks and cross-origin attacks referred to as [XS-Leaks](https://xsleaks.dev/).

Cross-origin isolated documents operate with fewer restrictions when using the following APIs:

- {{JSxRef("SharedArrayBuffer")}} can be created and sent via a {{DOMxRef("Window.postMessage()")}} or a {{DOMxRef("MessagePort.postMessage()")}} call.
- {{DOMxRef("Performance.now()")}} offers better precision.
- {{DOMxRef("Performance.measureUserAgentSpecificMemory()")}} can be accessed.

A website is in a cross-origin isolated state, when the response header {{HTTPHeader("Cross-Origin-Opener-Policy")}} has the value `same-origin` and the {{HTTPHeader("Cross-Origin-Embedder-Policy")}} header has the value `require-corp` or `credentialless`.
A document will be cross-origin isolated if it is returned with an HTTP response that includes the headers:

- {{HTTPHeader("Cross-Origin-Opener-Policy")}} header with the directive `same-origin`.
- {{HTTPHeader("Cross-Origin-Embedder-Policy")}} header with the directive `require-corp` or `credentialless`.

## Value

Expand Down
5 changes: 4 additions & 1 deletion files/en-us/web/api/window/open/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ open(url, target, windowFeatures)

### Return value

If the browser successfully opens the new browsing context, a [`WindowProxy`](/en-US/docs/Glossary/WindowProxy) object is returned. The returned reference can be used to access properties and methods of the new context as long as it complies with [the same-origin policy](/en-US/docs/Web/Security/Same-origin_policy) security requirements.
If the browser successfully opens the new browsing context, a [`WindowProxy`](/en-US/docs/Glossary/WindowProxy) object is returned.
The returned reference can be used to access properties and methods of the new context as long as it complies with [the same-origin policy](/en-US/docs/Web/Security/Same-origin_policy) security requirements.

If the {{httpheader("Cross-Origin-Opener-Policy")}} HTTP header is being used, and the document policies are such that the document is opened in a new {{glossary("Browsing context","browsing context group")}}, references to the opened window are severed and the returned object will indicate that the opened window is closed ({{domxref("Window.closed","closed")}} is `true`).

`null` is returned if the browser fails to open the new browsing context, for example because it was blocked by a browser popup blocker.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,23 @@ browser-compat: api.crossOriginIsolated

{{APIRef("Web Workers API")}}{{AvailableInWorkers("worker")}}

The **`crossOriginIsolated`** read-only property of the {{domxref("WorkerGlobalScope")}} interface returns a boolean value that
indicates whether the website is in a cross-origin isolation state. That state mitigates the risk of side-channel attacks and unlocks a few capabilities:
The **`crossOriginIsolated`** read-only property of the {{domxref("WorkerGlobalScope")}} interface returns a boolean value that indicates whether the document is cross-origin isolated.

A cross-origin isolated document only shares its {{glossary("Browsing context","browsing context group")}} with same-origin documents in popups and navigations, and resources (both same-origin and cross-origin) that the document has opted into using via [CORS](/en-US/docs/Web/HTTP/CORS) (and [COEP](/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) for `<iframe>`).
The relationship between a cross-origin opener of the document or any cross-origin popups that it opens are severed.
The document may also be hosted in a separate OS process alongside other documents with which it can communicate by operating on shared memory.
This mitigates the risk of side-channel attacks and cross-origin attacks referred to as [XS-Leaks](https://xsleaks.dev/).

Cross-origin isolated documents operate with fewer restrictions when using the following APIs:

- {{JSxRef("SharedArrayBuffer")}} can be created and sent via a {{DOMxRef("DedicatedWorkerGlobalScope.postMessage()")}} or a {{DOMxRef("MessagePort.postMessage()")}} call.
- {{DOMxRef("Performance.now()")}} offers better precision.
- {{DOMxRef("Performance.measureUserAgentSpecificMemory()")}} can be accessed.

A website is in a cross-origin isolated state, when the response header {{HTTPHeader("Cross-Origin-Opener-Policy")}} has the value `same-origin` and the {{HTTPHeader("Cross-Origin-Embedder-Policy")}} header has the value `require-corp` or `credentialless`.
A document will be cross-origin isolated if it is returned with an HTTP response that includes the headers:

- {{HTTPHeader("Cross-Origin-Opener-Policy")}} header with the directive `same-origin`
- {{HTTPHeader("Cross-Origin-Embedder-Policy")}} header with the directive `require-corp` or `credentialless`

## Value

Expand Down
135 changes: 128 additions & 7 deletions files/en-us/web/http/headers/cross-origin-opener-policy/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ browser-compat: http.headers.Cross-Origin-Opener-Policy

{{HTTPSidebar}}

The HTTP **`Cross-Origin-Opener-Policy`** (COOP) {{Glossary("response header")}} allows you to ensure a top-level document does not share a browsing context group with cross-origin documents.
The HTTP **`Cross-Origin-Opener-Policy`** (COOP) {{glossary("response header")}} allows a website to control whether a new top-level document, opened using {{domxref("Window.open()")}} or by navigating to a new page, is opened in the same {{glossary("Browsing context","browsing context group")}} (BCG) or in a new browsing context group.

COOP will process-isolate your document and potential attackers can't access your global object if they were to open it in a popup, preventing a set of cross-origin attacks dubbed [XS-Leaks](https://github.com/xsleaks/xsleaks).
When opened in a new BCG, any references between the new document and its opener are severed, and the new document may be process-isolated from its opener.
This ensures that potential attackers can't open your documents with {{domxref("Window.open()")}} and then use the returned value to access its global object, and thereby prevents a set of cross-origin attacks referred to as [XS-Leaks](https://xsleaks.dev/).

If a cross-origin document with COOP is opened in a new window, the opening document will not have a reference to it, and the [`window.opener`](/en-US/docs/Web/API/Window/opener) property of the new window will be `null`. This allows you to have more control over references to a window than [`rel=noopener`](/en-US/docs/Web/HTML/Attributes/rel/noopener), which only affects outgoing navigations.
It also means that any object opened by your document in a new BCG can't access it using [`window.opener`](/en-US/docs/Web/API/Window/opener).
This allows you to have more control over references to a window than [`rel=noopener`](/en-US/docs/Web/HTML/Attributes/rel/noopener), which affects outgoing navigations but not documents opened with {{domxref("Window.open()")}}.

The behaviour depends on the policies of both the new document and its opener, and whether the new document is opened following a navigation or using {{domxref("Window.open()")}}.

<table class="properties">
<tbody>
Expand All @@ -32,16 +36,102 @@ If a cross-origin document with COOP is opened in a new window, the opening docu
Cross-Origin-Opener-Policy: unsafe-none
Cross-Origin-Opener-Policy: same-origin-allow-popups
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Opener-Policy: noopener-allow-popups
```

### Directives

- `unsafe-none`
hamishwillee marked this conversation as resolved.
Show resolved Hide resolved
- : This is the default value. Allows the document to be added to its opener's browsing context group unless the opener itself has a COOP of `same-origin` or `same-origin-allow-popups`.
- `same-origin-allow-popups`
- : Retains references to newly opened windows or tabs that either don't set COOP or that opt out of isolation by setting a COOP of `unsafe-none`.

- : The document permits sharing its browsing context group with any other document, and may therefore be unsafe.
It is used to opt-out a document from using COOP for process isolation.
This is the default value.

On navigations, documents with `unsafe-none` will always open and be opened into a new BCG — unless the other document also has `unsafe-none` (or no COOP directive value).

Using `Window.open()`, documents with `unsafe-none` will always open documents with any other value into a new BCG.
However documents with `unsafe-none` can be opened in the same BCG if the opener has the directive `same-origin-allow-popups`, `noopener-allow-popups`, or `unsafe-none`.
A document with `same-origin` will always open a document with `unsafe-none` in a new BCG.

- `same-origin`
- : Isolates the browsing context exclusively to same-origin documents. Cross-origin documents are not loaded in the same browsing context.

- : The document permits loading into BCGs that use COOP and contain only same-origin documents.
This is used to provide [cross-origin isolation](/en-US/docs/Web/API/Window/crossOriginIsolated) for a BCG.

Documents with `same-origin` will only open and be opened in the same BCG if both documents are same-origin and have the `same-origin` directive.

- `same-origin-allow-popups`

- : This is similar to [`same-origin`](#same-origin) directive, except that it allows the opening of documents using {{domxref("Window.open()")}} in the same BCG if they have a COOP value of `unsafe-none`.

The directive is used to relax the `same-origin` restriction for integrations where a document needs the benefits of cross-origin isolation but also needs to open and retain a reference to trusted cross-origin documents.
For example, when using a cross-origin service for OAuth or payments.

A document with this directive can open a document in the same BCG using {{domxref("Window.open()")}} if it has a COOP value of `unsafe-none`.
In this case it does not matter if the opened document is cross-site or same-site.

Otherwise documents with `same-origin-allow-popups` will only open and be opened in the same BCG if both documents are same-origin and have the `same-origin-allow-popups` directive.

- `noopener-allow-popups`

- : Documents with this directive are always opened into a new BCG, except when opened by navigating from a document that also has `noopener-allow-popups`.
It is used to support cases where there is a need to process-isolate _same-origin_ documents.

This severs the connections between the new document and its opener, isolating the browsing context for the current document regardless of the opener document's origin.
This ensures that the opener can't run scripts in opened documents and vice versa — even if they are same-origin.

On navigations, a document with this directive will always open other documents in a new BCG unless they are same-origin and have the directive `noopener-allow-popups`.
Using {{domxref("Window.open()")}}, a document with this directive will open documents in a new BCG unless they have `unsafe-none`, and in this case it does not matter if they are same-site or cross-site.

## Description

Generally you should set your policies such that only same-origin and trusted cross-origin resources that need to be able to script each other should be allowed to be opened in the same browser context group.
Other resources should be cross-origin isolated in their own group.

The following sections show whether documents will be opened in the same BCG or a new BCD following a navigation or opening a window programmatically.

> [!NOTE]
> The specification uses the term "popup" to refer to any document opened using {{domxref("Window.open()")}}, whether it is a popup, tab, window, or other context.

### Navigations

When navigating between documents, the new document is opened in the same BCG if the two documents have "matching coop policies", and otherwise into a new BCG.

The policies match if:

- both documents are `unsafe-none`, or
- neither document is `unsafe-none`, their policy values are the same, and they are same-origin.

The table below shows the result of this rule on whether documents are opened in the same or a new BCG for the different directive values.

<!-- https://html.spec.whatwg.org/multipage/browsers.html#matching-coop -->

| Opener (row) / Opened (col) | `unsafe-none` | `same-origin-allow-popups` | `same-origin` | `noopener-allow-popups` |
| --------------------------- | ------------- | -------------------------- | ------------------- | ----------------------- |
| `unsafe-none` | Same | New | New | New |
| `same-origin-allow-popups` | New | Same if same-origin | New | New |
| `same-origin` | New | New | Same if same-origin | New |
| `noopener-allow-popups` | New | New | New | Same if same-origin |

### Opening with Window.open()

When opening a document using `Window.open()`, the new document is opened in the same BCG according to the following rules, which are evaluated in order:

1. True: opened `noopener-allow-popups`
2. False: (`opener same-origin-allow-popups` or `noopener-allow-popups`) and (opened document is `unsafe-none`)
3. False: Matching COOP policies (as outlined above for navigations)
4. True: Otherwise!

The table below shows the opener behaviour for the different directive values.

<!-- https://html.spec.whatwg.org/multipage/browsers.html#check-browsing-context-group-switch-coop-value-popup -->

| Opener (row) / Opened (col) | `unsafe-none` | `same-origin-allow-popups` | `same-origin` | `noopener-allow-popups` |
| --------------------------- | ------------- | -------------------------- | ------------------- | ----------------------- |
| `unsafe-none` | Same | New | New | New |
| `same-origin-allow-popups` | Same | Same if same-origin | New | New |
| `same-origin` | New | New | Same if same-origin | New |
| `noopener-allow-popups` | Same | New | New | New |

## Examples

Expand Down Expand Up @@ -70,6 +160,37 @@ if (crossOriginIsolated) {
}
```

### Severing the opener relationship

Consider a hypothetical origin `example.com` that has two very different applications on the same origin:

- A chat application at `/chat` that enables any user to contact any other user and send them messages.
- A password management application at `/passwords` that contains all of the user's passwords, across different services.

The administrators of the "passwords" application would very much like to ensure that it can't be directly scripted by the "chat" app, which by its nature has a larger XSS surface.
The "right way" to isolate these applications would be to host them on different origins, but in some cases that's not possible, and those two applications have to be on a single origin for historical, business, or branding reasons.

The `Cross-Origin-Opener-Policy: noopener-allow-popups` header can be used to ensure that a document can't be scripted by a document that opens it.

If `example.com/passwords` is served with `noopener-allow-popups` the `WindowProxy` returned by {{domxref("Window.open()")}} will indicate that the windows is closed ({{domxref("Window.closed")}} is `true`), so the opener can't script the passwords app:

```js
const handle = window.open("example.com/passwords", "passwordTab");
if (windowProxy.closed) {
// The new window is closed so it can't be scripted.
}
```

Note that this alone is not considered a sufficient security measure.
The site would also need to do the following:

- Use Fetch Metadata to block same-origin requests to the more-sensitive app that are not navigation requests.
- Ensure their authentication cookies are all `HttpOnly`.
- Ensure root-level Service-Workers are not installed by the less-sensitive app.
- Ensure that `postMessage` or `BroadcastChannel` on the more-sensitive app don't expose any sensitive information the any other same-origin app.
- Ensure their login page is served on a separate origin, due to password manager autofill being applied based on origin.
- Understand that the browser may still allocate the more-sensitive app in the same process as the less-sensitive one, making it vulnerable to Spectre-like attacks.

## Specifications

{{Specifications}}
Expand Down