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

Making postMessage() work for SharedArrayBuffer (Cross-Origin-Embedder-Policy) #4175

Closed
annevk opened this issue Nov 14, 2018 · 93 comments
Closed
Labels
addition/proposal New features or enhancements impacts documentation Used by documentation communities, such as MDN, to track changes that impact documentation security/privacy There are security or privacy implications topic: browsing context topic: cross-origin-embedder-policy Issues and ideas around the new "require CORP for subresource requests and frames and etc" proposal

Comments

@annevk
Copy link
Member

annevk commented Nov 14, 2018

#3740 (comment) sketches out v1 for the various headers needed to enable SharedArrayBuffer and friends.

At Mozilla we think we'll quickly need to address a need @arturjanc and @csreis hinted at. Being able to have cross-origin frames, either in the same process (e.g., because it's a process-contrained environment), or in a different process.

Our idea around this would be to add a new keyword to the Cross-Origin-Frame-Policy header:

Cross-Origin-Frame-Policy: same cors
Cross-Origin-Frame-Policy: same-site cors

If the cors keyword isn't set the v1 semantics apply, and cross-origin/site navigations result in a network error. If the cors keyword is set, the CORS protocol semantics apply to frame navigations. Judging from https://wicg.github.io/cors-rfc1918/ this could mostly be done through modifications to Fetch, which makes this less difficult than I initially anticipated. Navigations should also never require a preflight, therefore only requiring modifications to the final resource on servers (and redirects, if any).

A risk here for the embedder is that the embedded could redirect or navigate to an attacker. https://w3c.github.io/webappsec-cspee/ and sandboxing can be used to mitigate this, similar to how you'd combat XSS in your own document.

The short term advantage is that we could have something that works in all browsers more quickly, the long term advantage would be potentially saving on resource usage. And more speculatively this kind of trust relationship might also be beneficial to other APIs.

We'd like to implement this shortly after or together with v1.

Note that none of this has an effect on the WindowProxy/Location same origin-domain check. That will continue to consider such frames as being in a different origin.

cc @whatwg/security @rniwa @tomrittervg

@annevk annevk added addition/proposal New features or enhancements security/privacy There are security or privacy implications topic: browsing context impacts documentation Used by documentation communities, such as MDN, to track changes that impact documentation labels Nov 14, 2018
@csreis
Copy link

csreis commented Nov 15, 2018

Thanks for proposing this! If I follow correctly, this would basically allow SharedArrayBuffer-enabled documents to load cross-origin iframes as long as the iframe requests use CORS (in the spirit of X-Bikeshed-Allow-Unisolated-Embed). This would imply that the cross-origin iframe's server would acknowledge that such data would be accessible to the embedding page, and thus it's safe to include in the same process even if SharedArrayBuffers or other precise time features make Spectre attacks more likely. (Meanwhile, browsers with support for cross-process frames could load such iframes in a different process without diverging in behavior from the user's perspective.)

I'm not 100% sure ad sites (etc) would go for this "full access" approach (where the embedding page could just as easily fetch the iframe URL and read its contents), but it does seem to make explicit that the data could be read with Spectre, and thus conveys the risks in a reasonable way. I imagine it might be sufficient.

I think this would resolve my main concern from the other thread (#3740 (comment)). @arturjanc, does this sound reasonable from your perspective?

@mikewest
Copy link
Member

Navigations should also never require a preflight, therefore only requiring modifications to the final resource on servers (and redirects, if any).

For my own clarity, I think this is what you're proposing:

  1. The embedding document asserts Cross-Origin-Frame-Policy: cors.
  2. The embedding document embeds <iframe src="https://cross-origin.site/">.
  3. The browser requests https://cross-origin.site/, sending an Origin and
  4. If https://cross-origin.thing/ responds with Access-Control-Allow-Origin: https://embedder.site/ and Access-Control-Allow-Credentials: true, we allow the embedding. Otherwise, we return a network error.

Is that right?

It's not clear to me what capabilities this would expose. Like, does the embedder get DOM access to the embedee? That seems like something we'd want to avoid. As @csreis notes, this also seems to create the risk that page contents are directly revealed via fetch(), which also seems like something we'd want to avoid.

CORS-RFC1918 took a different approach, forcing a preflight, but not forcing CORS access to the page itself. That might be an interesting approach here as well, as it would reduce the risk that page content would be inadvertently exposed to an embedder by making it opt-in (via the preflight response), but not directly exposing the page data to the embedder (by not requiring CORS headers on the non-preflight response itself).

@annevk
Copy link
Member Author

annevk commented Nov 15, 2018

It's not clear to me what capabilities this would expose.

It mainly would allow for process reuse on process-constrained environments, as stated in OP.

Like, does the embedder get DOM access to the embedee?

See last paragraph of OP.

As @csreis notes, this also seems to create the risk that page contents are directly revealed via fetch()

It's not clear to me how much it's worth trying to distinguish that case from putting something in the same process. It seems it might give a false sense of security.

@arturjanc
Copy link

For opt-in, I like @mikewest's preflight-based approach from https://wicg.github.io/cors-rfc1918/#shortlinks

This should allow the owners of cross-origin resources to respond to OPTIONS requests with something like Access-Control-Allow-Embed: true without also allowing direct reads of response contents via fetch(). I sympathize with the concern about giving server owners a false sense of security, but would prefer to avoid encouraging server owners to allow direct access to their responses (e.g. we don't want ad network resources to be directly readable via CORS since that would reveal interesting information about users).

There is a possibility that Spectre-like attacks would be able to exfiltrate the contents of the frame, but they would likely be less powerful and noisy; and, importantly, they wouldn't work against browsers with OOPIFs (whereas if we require regular CORS opt-in via Access-Control-Allow-Origin then we would expose the contents also in browsers with OOPIFs where introducing the leak isn't necessary.)

A benefit of this approach is that it would enable identical behavior across browsers: on pages with Cross-Origin-Frame-Policy all browsers would send a preflight on cross-origin iframe requests and would require the server to opt in. However, as browsers adopt OOPIFs the responses would become safe against exfiltration via speculative execution attacks, while still requiring the server to opt into embedding (which is okay because servers will have to do this in the short term anyway).

@arturjanc
Copy link

Also, stepping back a bit, do we even need the same / same-site switch in this case? I think we can allow same-origin frames by default, and for any non-same-origin framing requests we could use the CORS / preflight approach to require resource owners to opt in. This would also be safer because we would prevent foo.example.org from declaring itself eligible to frame bar.example.org while getting access to high-res timers or other dangerous APIs, allowing it to potentially exfiltrate cross-origin contents from its sibling subdomain.

If we did this, then the header could just become Cross-Origin-Frame-Policy: 1 (or some other more descriptive name/value.)

Also, to push this idea as far as possible, could we even completely fold this into the Upgrade-No-CORS header? Then the header would require sending all non-same-origin subresource requests as CORS and do the same for iframes as discussed above. It would also have to apply recursively to all frames, but this should be okay because, as outlined above, servers would have to opt in or otherwise the frame would not be loaded.

I think this might be conceptually simpler for developers while giving us all the security properties we need for the L2 mechanism.

@annevk
Copy link
Member Author

annevk commented Nov 20, 2018

  1. If you don't do "true" CORS for navigations, I don't think it's reasonable for browsers to same-process these frames long term. That might be okay, but wouldn't be ideal for process-contrained environments.
  2. Upgrade-No-CORS is specifically named after Fetch's "no-cors" mode. Navigations use the "navigate" mode, which is similar, but different. Also, per your proposal navigations wouldn't use "true" CORS, they'd only preflight.

So I think I agree with your plan, but we need a new name for the header. Perhaps Use-CORS, with the explanation that for subframe navigations this means a specific kind of preflight only.

So, why not also use this approach for popups you want to cooperate with?

@annevk
Copy link
Member Author

annevk commented Nov 23, 2018

So while trying to explain this model to someone over lunch I realized this allows for escaping the "Use-CORS" restriction in browsers without process-isolated frames/popups.

attacker.example specifies Use-CORS and the correct opener policy. It loads collaborator.attacker.example in a frame/popup and that positively replies to the preflight. collaborator.attacker.example isn't restricted itself by CORS however can load all kinds of "no-cors" resources into the process.

It seems to me we need to require that collaborator.attacker.example also specifies Use-CORS.

@annevk
Copy link
Member Author

annevk commented Nov 23, 2018

I talked with @arturjanc, his assumption was that Use-CORS is inherited cross-origin in these cases, which is probably acceptable as the navigation response opted into it via the preflight.

@annevk
Copy link
Member Author

annevk commented Nov 23, 2018

Not discussed yet: if you restrict to same-origin, what are the implications for document.domain (also raised at #3740 (comment)) and SharedArrayBuffer? It seems those would not work then cross-origin, but same-site. It's not entirely clear to me if that's desirable. (It's ideal, but...)

@annevk
Copy link
Member Author

annevk commented Jan 4, 2019

I'm no longer entirely convinced we need the preflight.

  1. I'm worried it creates an unacceptable performance penalty.
  2. It doesn't seem to offer any additional protection. It was inspired by the local network proposal where it does make sense, but CSRF will remain a problem here and has to be dealt with differently.

I think instead the model should be such that once a browsing context group has its "Use-CORS" flag set, any resource navigated to within that group needs to have the Use-CORS header set. And if not, the network layer will return a network error. I'm not entirely sure if we should require redirects to have this header set or not. (A redirect can be navigated to if it doesn't have a Location header or the value of that header cannot be parsed. In that particular case it definitely needs to have the header set, but I'm less clear on when we simply follow it to somewhere else.)

Then, there's the question of credentials. Other than with fetch() (which defaults to "same-origin" for credentials), "no-cors" fetches will always include credentials across origins. So we need to at least support the equivalent of HTML's crossorigin="use-credentials" (this made me think that Cross-Origin: use-credentials as header might not be too bad). Whether we also need crossorigin="anonymous" is less clear, but that would allow for a less complicated CORS setup.

If we allow variance in credentials, it probably does not make sense to require it to match across documents. It's reasonable for different documents (esp. cross-origin) to have their own "no-cors" credentials policy.


For a moment I was worried about service workers and the cache API being able to introduce opaque responses into "Use-CORS" documents. However, this concern is probably unfounded. A service worker is handled by it not being able to return opaque responses to "cors" fetches (we'll upgrade before hitting the service worker). The cache API will need to be restricted from returning opaque responses in "Use-CORS" environments somehow. Currently you cannot do anything with such responses anyway in documents so maybe that's enough (assuming an implementation that leaves the bytes in the "storage process" until requested), but we'll need to be cautious going forward. An alternative is to prevent them from being returned altogether when the top-level flag is set.

@annevk
Copy link
Member Author

annevk commented Jan 4, 2019

When does Use-CORS/Cross-Origin take effect:

  1. It initially needs to be a on a resource that results in the creation of a new browsing context group. I.e., one that has Cross-Origin-Opener-Policy set.
  2. To be clear, this Cross-Origin-Opener-Policy requirement doesn't apply to further navigated resources loaded in that browsing context group. For those only this new second header matters (except for top-level browsing context navigations with a non-matching Cross-Origin-Opener-Policy).

As for document.domain:

  1. The simplest thing to do I think is to continue to allow document.domain and to continue to key agent clusters on sites. Same-site resources can only be attacked if they opt in via CORS or this new second header though (assuming they're navigable).
  2. The ambitious thing to do is to require Cross-Origin-Opener-Policy: same-origin ... when using this new second header and change the browsing context group's agent cluster keying such that the key is now origin, effectively disabling document.domain. Unless folks are particularly motivated to do this exercise, this seems unlikely to happen. Attempts at making document.domain worse in the past have largely failed and tightly coupling the worsening with important new features puts the new feature at risk.

@annevk
Copy link
Member Author

annevk commented Jan 8, 2019

A thing we haven't really discussed or at least written down in these threads is how SharedArrayBuffer is enabled. I propose that SharedArrayBuffer is always there, but only agent clusters with a flag set allow it to be messaged between agents. This means that the ECMAScript standard can continue to say it's always exposed and HTML (the host) will impose the limitation on usage. By not allowing it to be messaged it's effectively equivalent to and no more dangerous than ArrayBuffer.

Making StructuredSerializeInternal throw ("DataCloneError") when it's invoked in an agent cluster that wasn't created under the right circumstances should be sufficient for this I think.

@annevk annevk changed the title Cross-Origin-Frame-Policy v2, enabling cross-origin frames Opting into a CORS-only mode (Cross-Origin) Jan 18, 2019
@annevk
Copy link
Member Author

annevk commented Jan 18, 2019

Feedback and attempts to address it:

  • How do sites know about being included? I think we should reuse the Origin header here. This already works for CORS, it seems fine to reuse here. As Origin is used for CORS purposes, Referrer Policy cannot affect it (for subframe and popup navigations). Currently Origin is not included for GET navigations, so this should work I think (and server-side needs changes anyway due to the response headers).
  • Should we require X-Frame-Options? This was suggested as Cross-Origin would be used for all responses so if you don't consider the possibility of being framed, someone might end up framing you. It seems somewhat reasonable, but I'm a little wary of adding this additional complexity. (Remember that currently Chrome requires none of this.) We don't have to add allow from * necessarily as you could echo the origin value after allow-from (similar to what we require with CORS). Potential issues:
    • same-origin is spelled sameorigin (case-insensitive too) and there's no same-site as we have elsewhere.
    • There's an equivalent feature in CSP. Would we also make that work and increase the complexity even more? I'd hope not.
    • I think there are some interoperability issues around allow-from.
  • Does Cross-Origin work in a browsing context group without Cross-Origin-Opener-Policy? Nika argued that it should and apply to the subtree. I think that's fine. We do have to be careful here with what other APIs build on top of this as this isn't necessarily a secure primitive (i.e., nothing should prolly be build on top of this). It would mainly give consistency in loading resources.

@annevk

This comment has been minimized.

@annevk

This comment has been minimized.

@csreis

This comment has been minimized.

@jakearchibald
Copy link
Contributor

jakearchibald commented Feb 8, 2019

Can a Use-CORS page contain an iframe to a cross-origin page that doesn't have Use-CORS? What happens if a SharedArrayBuffer is postMessaged to the iframe?

Update: BroadcastChannel and the service worker clients API creates the same problem with same-origin pages.

@annevk
Copy link
Member Author

annevk commented Feb 9, 2019

https://gist.github.com/annevk/17f580379c45802d5c3aef5a8fd53c7d has more details on the processing model. Feedback welcome!

@jakearchibald the iframe case would result in a network error for the frame. BroadcastChannel does not pose a problem as those pages would be in different agent clusters (they'd get the messageerror event). Service workers are also in their own agent cluster.

@clelland
Copy link
Contributor

clelland commented Feb 21, 2019

Edit: Updated link

I'm wondering if it would be possible to extend this with a mode which simply set all fetch requests' credentials modes to 'omit', without also upgrading to CORS?

Cross-Origin: omit

This could allow sites to adopt the Cross-Origin header to enforce that they do not ever request any user-specific data from third parties, but would still be able to link to anonymous public resources, cache images and scripts on CDNs, and preserve the more-or-less free embedding that the web has always had.

I had written up a proposal along those lines here a few days ago, which seems very similar in spirit to this.

@csreis
Copy link

csreis commented Feb 22, 2019

Thanks for mentioning, @clelland! I think the credential-less mode is worth considering, as it would allow sites to pull in effectively public third party subresources without needing CORS on them, and thus impose fewer restrictions without giving up much of the security value. (Presumably documents could optionally request credentialed subresources with CORS if they wanted them.) I also imagine that would be easier to eventually enable by default than CORS-only, and using it here for enabling precise timers might be a step in that direction.

The main hole is probably intranet resources, but maybe something like RFC1918 can help (cc @mikewest)?

What are others' thoughts on full CORS vs credential-less requests?

@csreis
Copy link

csreis commented Feb 22, 2019

Adding @bzbarsky and @ehsan, who brought up similar ideas about a credential-less mode (or default) in the past. Requiring it to enable precise time (perhaps instead of CORS for all cross-site subresources, as we've been discussing here?) might be a nice step towards making credential-less subresources be the default, which would help cover the cases CORB misses today.

@annevk annevk changed the title Opting into a CORS-only mode (Cross-Origin) Making postMessage() work for SharedArrayBuffer (Cross-Origin-Embedder-Policy) Jun 23, 2019
@arturjanc
Copy link

It doesn't seem necessary for COEP to inherit into auxiliary documents, see Issue 4 in §3.1.1 in Mike's doc. If we required it then documents with SAB couldn't open popups to non-cooperating cross-origin documents, even though this would be safe because COOP would put them in a new browsing context group (it also seems inconsistent because presumably we'd allow regular navigations from the main COOP+COEP document to another document without the headers, relying on COOP to force a process swap).

IMHO it's also cleaner for COEP to only affect resource loads within the document and its iframes (primarily for the sake of browsers without OOPIFs) and leave it to COOP to put top-level documents in different browsing context groups; this should still give us the security properties we're looking for.

@annevk
Copy link
Member Author

annevk commented Jun 25, 2019

I think I'm okay with the model where COOP is effectively in charge as to whether or not a new browsing context group is to be created, while having to check COEP occassionally when doing so in order to make the right decision. This would indeed not work if unsafe-allow-outgoing would not disable SharedArrayBuffer as then COEP would also have to be in charge (as we'd have top-level documents without COOP enforcement).

@mikewest
Copy link
Member

Ok. I'll update the COEP doc, and take a stab at a COOP doc. @arturjanc said he was willing to write a developer-facing threat model / process model doc. Maybe these will all end up being the same doc? Who knows.

I think we're pretty close to turning these into PRs and tests. :)

@rniwa
Copy link

rniwa commented Jun 25, 2019

This has been a very long discussion and it's getting hard to follow what the latest proposal & consensus are. Can someone post a summary of the current COEP / COOP proposal?

@arturjanc
Copy link

I started putting together a developer-facing COEP + COOP => SAB explainer of sorts, and should have something by tomorrow (it won't be perfect but it will hopefully be a starting point for something usable).

@annevk
Copy link
Member Author

annevk commented Jul 2, 2019

I have updated https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e (Cross-Origin-Opener-Policy description) to account for unsafe-inherit (see #4581) and Cross-Origin-Embedder-Policy. In particular, how and when Cross-Origin-Embedder-Policy influences the "match". I hope this can help Mike's document and it might help Ryosuke though I think Artur will also post his high-level document soon.

@arturjanc
Copy link

Apologies for the delay... Thanks to @mikewest's COEP spec and with a lot of help from @csreis and @annevk I jotted down a summary of the current proposal for COOP+COEP, along with some developer guidance for deploying the headers in this doc. This is by no means final, but should hopefully capture the main points from the various assorted discussions we've had -- please take a look and comment in the doc.

@Malvoz
Copy link
Contributor

Malvoz commented Jul 3, 2019

In the document:

Cross-origin resources can opt in via CORP (with a Cross-Origin-Resource-Policy header with a value which includes the requester, e.g., same-site, or cross-site for public resources)

But for CORP only same-site and same-origin is defined. So rather than cross-site it'd be the absence of a CORP header?

@annevk
Copy link
Member Author

annevk commented Jul 3, 2019

No, that's a new value. Absence of the header when COEP is in effect would result in a network error.

@arturjanc
Copy link

A small caveat to the above is that the CORP requirement would apply only to non-same-origin resources. Loading same-origin resources would be allowed when COEP is in effect under (3) in §3.2.1 (same-origin iframes would still need to set COEP, though.)

CORP cross-site is defined in https://mikewest.github.io/corpp/#integration-fetch

@annevk
Copy link
Member Author

annevk commented Jul 8, 2019

While thinking about tests (if you want to help, please see web-platform-tests/wpt#17606 for material to review and contribute to):

  1. I think we should make workers specify COEP directly rather than inherit it and have enforcement be more similar to frames.
  2. I wonder if we should allow style sheets to specify a policy as well, similar to https://w3c.github.io/webappsec-referrer-policy/#integration-with-css. The main advantage would be that if you share your style sheets with others, they have a consistent fetching policy throughout. (This does not really work for scripts (except perhaps for imports).)

@clelland
Copy link
Contributor

Somewhere along the way here I lost track of where credentialless subresources fit in the model -- has that been dropped entirely, or is there some way to achieve it with COEP/CORP?

I'd love to be able to use this to declare that certain subresources are public, having been fetched over the public internet (per CORS-1918), without any cookies/credentials attached.

I don't see it in the current proposal, but I could imagine something like Cross-Origin-Embedder-Policy: no-credentials being used to force that mode.

@annevk
Copy link
Member Author

annevk commented Aug 9, 2019

That's not part of the MVP and given the complexity of the "public internet" bit I don't think it should be. I'd support experimenting with it and iterating on it in parallel though. Would you mind opening a new issue?

@annevk
Copy link
Member Author

annevk commented Jul 8, 2020

This was fixed by a mixture of 70b8bb4 and c9d8983.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements impacts documentation Used by documentation communities, such as MDN, to track changes that impact documentation security/privacy There are security or privacy implications topic: browsing context topic: cross-origin-embedder-policy Issues and ideas around the new "require CORP for subresource requests and frames and etc" proposal
Development

No branches or pull requests

10 participants