Closed rniwa closed 4 years ago
@annevk: Thanks for elaborating! And will it be permitted to ignore Cross-Origin-Frame-Policy if the browser can put such frames into a different process? Just want to make sure the spec isn't requiring that the navigation fails if it can be done safely.
@csreis I'm not sure if that makes sense, since you really need both that and Upgrade-No-CORS
. Enforcing Upgrade-No-CORS
on cross-origin documents seems problematic in a way.
I agree with Upgrade-No-CORS being problematic for cross-origin iframes-- that could possibly be ignored in cross-process subframes as well, or we could look for a different way to approach this restriction.
Mainly, I think we need a way to avoid unnecessary long-term restrictions on pages that request high resolution timers. I can see how Upgrade-No-CORS is useful for those documents and any descendants in the same process (even in the long run), but it stops being necessary for cross-origin subframes if/when browsers can load them in a different process.
Can we design this in a way that allows for it to gracefully upgrade, allowing cross-origin subframes to load in a different process without the CORS restriction?
Yeah sorry, my response needed more nuance, Upgrade-No-CORS
would only need to "inherit" within the agent cluster (which by definition is same-site restricted). The main subtlety where you'd need both I think is if you want same-origin and not same-site. Since cross-origin, but same-site, will be in the same agent cluster and thus vulnerable in a way. (We could also chose not to offer same-origin protection for now I suppose.)
Sorry for the delayed reply. I'm not sure if that answered my main question, which is whether we will allow cross-origin iframes in pages that want precise time, if the browser can support cross-process frames.
Two possible ways we might do that:
1) Cross-Origin-Frame-Policy always prevents cross-origin iframes (and thus the Upgrade-No-CORS issue is moot). However, browsers with cross-process frame support can choose to grant access to SharedArrayBuffers, etc, without requiring Cross-Origin-Frame-Policy. Sites can choose whether to include the Cross-Origin-Frame-Policy header based on User Agent. (This is arguably not great, because it requires sites to know which browsers will give them SharedArrayBuffers with and without Cross-Origin-Frame-Policy.)
2) Cross-Origin-Frame-Policy itself can be ignored by browsers with cross-process frames, and Upgrade-No-CORS only applies within the agent cluster. Sites can then serve the Cross-Origin-Frame-Policy header and put cross-origin frames in their page, with the understanding that browsers will display those frames only if it can be done safely (i.e., in a different process). This means the Cross-Origin-Frame-Policy header may lose its meaning over time if more browsers get support for cross-process frames, but so far I haven't heard a strong reason to keep it after cross-process frames are possible.
Would we be ok with the latter?
I don't think (2) makes sense. Preventing descendent frames in your page to navigate to an URL outside of your own origin/site is a useful feature on its own. What we can do is that once all major browsers have supported per-frame process isolation, then we can enable high precision timers & shared array buffers without these headers.
I don't think we can simultaneously allow some UAs to start exposing high precision timers, and at the same time no cross-UA behavior difference since those two things are fundamentally at odds. Fundamentally, UAs that support per-frame process isolation would allow high precision timers in contexts where other UAs won't, or all UAs are stuck with not enabling high precision timers until all UAs support per-frame process isolation if we wanted the interoperable behaviors across UAs.
Either way, I don't think some UAs ignoring Cross-Origin-Frame-Policy
would make much sense because that's no worse than (2) in terms of interoperability and eliminates the benefit of this header.
Note that I think we should always require Upgrade-No-CORS
for high resolution timers of any kind (including SharedArrayBuffer
). Otherwise attacking resources (that fall outside the CORB safelist) with a high-bandwidth channel is just too easy.
Given what @annevk said above, I think we have rough agreement about the behavior we want from the L1 and L2 mechanisms, so perhaps it's a good time to have a pie fight about the spelling? ;-) Here are some comments, using https://github.com/whatwg/html/issues/3740#issuecomment-433945551 as the baseline proposal.
My main thought is that for L2 it might make sense to combine Upgrade-No-Cors
and Cross-Origin-Frame-Policy
into a single header. Given that in https://github.com/whatwg/html/issues/4175 we're considering CORS as the mechanism that would let servers consent to iframing by an L2-isolated document, both of the headers boil down to requiring CORS for cross-origin loads (subresources and frames, respectively). Given that we need both restrictions to enable high-res timers, it might be confusing to expose two separate switches where one suffices:
Require-CORS: true
which enforces the use of CORS for non-same-origin subresources and frame loads and propagates through descendant iframes to be a fairly straightforward switch that would be sufficient for most applications.For the Cross-Origin-Opener-Policy
proposal, my main comment is along the lines of (2) above -- sameness seems a little more useful in this context, but I'm still not sure if this is a switch that developers really need. Similar to Ryosuke's proposal above and Nika's comment, I'd find COOP: deny-incoming | deny-outgoing | deny-all
to be fairly straightforward (though I agree with Anne that it could be a blocker in some scenarios; my hope is that it's more of a niche use case, but I could certainly be convinced otherwise.)
In this world:
COOP: deny-incoming
+ CORP/SameSite cookies/Sec-Metadata to protect any server responses that aren't covered by CORB.COOP: deny-all
COOP: deny-all
and Require-CORS: true
and would give access to SharedArrayBuffer
and potentially other dangerous features.Would something like this make sense? (Apologies in advance if I omitted any important considerations from the discussions above.)
I prefer the allow-outgoing
design in that you have to be explicit in what to allow. It's also not clear to me that deny-outgoing
on its own is something we want or discussed.
It also seems that if we can do CORS preflights for navigations, we can do those for popup navigations too and should perhaps allow allow-outgoing
in L2, provided that mechanism is in use.
Today's Hangout summary:
https://github.com/whatwg/html/issues/3740#issuecomment-433945551 is the current consensus with a clarification that navigation to and from a document with Cross-Origin-Opener-Policy
would always result in a new browsing context group. allow-outgoing
would only apply to navigations within auxiliary browsing contexts it opened. We'd leave an informal note saying that the author should be deploying CSP navigate-to
directive when using allow-outgoing
.
We also agreed on renaming allow-outgoing
to unsafe-allow-outgoing
to signify the risk of letting others have a reference to you (and be in your process in some implementations).
Cases that I don't think we explicitly discussed:
I don't have strong feelings either way, so my guess is that we should aim for conceptually the simplest behavior. To me, it would make sense if the header was always ignored in a nested browsing context (so B would have the same behavior regardless of whether it sets the header). When B opens a pop-up, it seems reasonable for it to obey the unsafe-allow-outgoing value set on A -- this introduces a minor leak (A can know if B opened a new window), but this doesn't seem much worse than the status quo. Just my two cents, though...
One other thing that came up earlier but that AFAIK we haven't fully resolved is the Upgrade-No-CORS
header and whether to automatically supply credentials on upgraded requests. I think we'd need to do it to not require significant refactoring of code/markup which loads authenticated subresources; but this behavior could be a little surprising, so we should probably put some thought into this.
Okay, so tentatively for Cross-Origin-Opener-Policy
:
Let's discuss CORS in #4175. Mozilla still intends to ship both together, but given the reservations stated in the meeting it might be good to somewhat separate the discussions for now.
(I didn't realize this originally covered CORS as well, my bad. I think it would make sense to keep this for Cross-Origin-Opener-Policy
and the other issue for enabling SharedArrayBuffer
.)
Having written up a more formal description for COOP I realize I made some minor errors above. In particular there's no need for this policy to be on the browsing context group. Either auxiliary browsing contexts are allowed (if the unsafe flag is set) or they create new browsing context groups. So we only need changes to navigating a top-level browsing context (includes auxiliary, to be clear) and creating an auxiliary browsing context (might have to create a new browsing context group instead). Hope this new description aides in review.
I updated the description for COOP linked above with initial about:blank document handling and redirect handling. I think from a HTML Standard perspective we could also make "fast back" work by allowing browsing contexts to be closed/opened in addition to being discarded. We'd close it when replacing it with BCG and open it when going back. I'm not sure if this is worth specifying for v1 (although maybe it turns out to be easier), but as "fast back" is optional anyway I don't think it matters much.
The way the about:blank handling for unsafe-allow-outgoing should work out such that a navigation from initial about:blank ends up with replacement if there's a non-matching COOP on the response. I.e., if there's no COOP on the response, no replacement happens and you get an unsafe reference. If it's a "sameness" navigation and the response has a matching COOP, no replacement happens and you get a reference as well, because about:blank inherits COOP from its creator. A further navigation from that sameness response with COOP to a non-matching COOP would yield replacement. That seems like the right model since otherwise setting the header would sometimes not give the isolating effect it promises.
While working on #4284 I realized that non-auxiliary top-level browsing contexts can also get assigned external state:
Cross-Origin-Opener-Policy
is specified we should get rid of the name. This has no negative consequences that I can think of. The site itself can still set it.Cross-Origin-Opener-Policy
set we run into this. If A used noopener
we also run into this and in that case we copy over the flags. So we'll have to support creating a new browsing context group with some inherited flags, but it would be nice if Cross-Origin-Opener-Policy
meant no inherited state, so I'd propose we network error here given that it's a nice cache.(Apologies for originally posting these observations in the wrong issue. Thanks @csreis for the correction. I've marked the corresponding comments in the other issue as off-topic.)
Would the network error for sandbox documents only apply to top-level contexts, both non-auxiliary and auxiliary? If so, this is probably okay as they are indeed a fairly niche case -- otherwise, if we also network error'ed sandbox frames, it could be a larger obstacle to adoption, e.g. if the site has ads.
FWIW there are scenarios where maliciously putting a cross-origin document in the sandbox can be used in attacks (e.g. open a document from victim origin in a sandbox window which doesn't allow modals to prevent an alert() from warning the user about some unexpected condition). So the "easy option" of responding with a network error here might actually be security-positive.
Cross-Origin-Opener-Policy only takes effect in top-level, so yes. For nested contexts you could use X-Frame-Options. If you think further exploring dedicated anti-sandbox features are worth it, let's do that in a separate issue (it seems reasonable to me by the way, I was somewhat surprised we never really considered this before)?
A thing we have not discussed in detail here, though @mystor did mention it in https://github.com/whatwg/html/issues/3740#issuecomment-399194225 is history.
Firefox's current approach is history is copied somewhat into the new browsing context group, so history.back()
et al do function to some extent. My personal feeling is that the UX for history should continue to work, but API-wise it should feel as if the user navigated directly to your (COOP) document.
What are Chrome and Safari considering here?
A thing we have not discussed in detail here, though @mystor did mention it in #3740 (comment) is history.
Firefox's current approach is history is copied somewhat into the new browsing context group, so
history.back()
et al do function to some extent. My personal feeling is that the UX for history should continue to work, but API-wise it should feel as if the user navigated directly to your (COOP) document.What are Chrome and Safari considering here?
For Chrome, we're keeping the session history intact across browsing context groups, both for the back/forward buttons and history.back() et al. Chrome manages the session history for a tab in the browser (parent) process, with a notion of browsing context group (BrowsingInstance) and process (SiteInstance) associated with each session history item.
I think the desired behavior here is what @annevk described in https://github.com/whatwg/html/issues/3740#issuecomment-486170969: we want history navigations via the browser UI to work, but ideally they wouldn't be accessible via window.history
.
A risk with allowing access to history from JS is that upon a navigation from a site with COOP to an attacker's document the browser may leak interesting information (e.g. an attacker can get information about the number of navigations on the victim site via window.history.length
).
If it adds significant implementation / spec difficulty then the severity of the leak is likely not worth addressing, but there are some benefits to removing JS access to history in this case.
As a result of some related issues, in particular how inheritance ought to work and whether unsafe-inherit
would be sufficient if an OAuth-chain had cross-site documents in it as well, we ended up simplifying Cross-Origin-Opener-Policy
a bit. It now has an explicit default, unsafe-none
, which is mostly there to potentially allow for changing the default at a future point (and allow sites to opt out of such a change). And the remaining values are same-origin
and same-origin-allow-popups
. Given the problems with unsafe-inherit
there seemed to be less of a need for same-site
-like policies, but adding those back in would not be a lot of work if developers request them.
https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e is still the canonical document and I've updated it with these changes.
The Cross-Origin-Opener-Policy header seems to be quite similar to what the rel="noopener noreferrer" attribute does when opening a new document in a new tab (target="_blank").
When should I use which one? It seems the COOP header is applicable when I link between origins while the rel="noopener noreferrer" attribute (on anchor tags) seems to work on the same origin as well?
Should I use both? They seem to be quite complimentary.... Some advice here would be nice.
Proposal
Add HTTP header called
Cross-Origin-Window-Policy
, which takes a value ofDeny
,Allow
, andAllow-PostMessage
.When the HTTP response of a document has a
Cross-Origin-Window-Policy
header, and the value case-insensitively matchesDeny
ignoring cases, the document is said to be fully isolated. If the value case-insensitively matchesAllow-PostMessage
ignoring cases, the document is said to be isolated with messaging. If the value doesn't match either or isn't set, then the document is said to be not isolated. If a document is fully isolated or isolated with messaging", it is said to be isolated*.In a fully isolated document, access to any property on the window proxy of a cross-origin document (regardless of whether the target document is fully isolated or not) results in a
SecurityError
. In a document isolated with messaging, access to any property exceptpostMessage
on the window proxy of a cross-origin document results in aSecurityError
. The restriction between two documents are symmetrical and the most stricter of the two prevails.Furthermore, a new step is inserted into the concept of allowed to navigate before step 1: If B and/or A is isolated and A and B are not of the same origin, return false.
Examples
Let document A and document B be distinct documents its own browsing contexts. If A and B are of the same origin, the header has no effect. If A and B are cross-origin, then:
If document B is fully isolated and document A is not isolated. Any attempt to access a property on document B's window from document A results in a
SecurityError. Any attempt to access a property on document A's window from document B also results in a
SecurityError`.If document B is isolated with messaging and document A is not isolated. Any attempt to access a property except
postMessage
on document B's window from document A results in aSecurityError
. Any attempt to access a property exceptpostMessage
on document A's window from document B results in aSecurityError
.If document B is isolated with messaging and document A is fully isolated. Any attempt to access a property on document B's window from document A results in a
SecurityError
. Any attempt to access a property on document A's window from document B results in aSecurityError
.If document B is isolated with messaging and document A is isolated with messaging. Any attempt to access a property except
postMessage
on document B's window from document A results in aSecurityError
. Any attempt to access a property exceptpostMessage
on document A's window from document B results in aSecurityError
.Spectre Protection Plan
For the purpose of protecting a website
a.com
from Spectre in browsers which support process swap for top-level navigations without frame-level process isolation,a.com
can set this header on all of its documents (not setting on some would result in leaks; more on this later).If this header is set on
a.com
, we can swap process on cross-origin navigation from or toa.com
's documents because this header guarantees thata.com
doesn't have access to any other document outside of its origin, and vice versa.Let's say we're on some page B1 in
b.com
, and it window.open'ed (isolated)a.com
. Thenb.com
doesn't have access toa.com
, andb.com
doesn't have access toa.com
so we can put them into two different processes. Obviously,a.com
's iframes don't have access tob.com
's frame tree either so if a website is currently relying on being able to do this, they won't be able to use this header.Let's say now
a.com
is navigated to some other page B2 inb.com
. In this case, the browser finds the process which loaded B1 and load B2 in the same process so that they can talk to one another via window proxies.