When a web application opens another origin in a window, it obtains a JavaScript reference to that context that it can reach through to poke at various things. The opened context likewise receives a reference to its opener which provides similar access. This communication channel between the two windows enables attacks both at the web API level (postMessage
vulnerabilities, navigation trickery, and numerous side-channels), and at the process level (without OOPIF, there are clear issues, but even with OOPIF document.domain
makes it difficult to put cross-origin-but-same-site documents into distinct processes).
These attacks are particularly interesting, as top-level navigation via newly opened windows will include SameSite=Lax
cookies. This means that origins remain vulnerable to side-channel attacks that rely on user authentication (for example, frame counting) even in browsers that shift cookies to that more-secure default.
Web developers can defend themselves against these attacks by opting-into Cross-Origin Opener Policy, which can break the link between cross-origin windows by putting them into distinct browsing context groups. That is, when the clever developers at secrets.example
assert a COOP, calling window.open("https://secrets.example/")
from a cross-origin page will return a closed window handle, useless for side-channel attacks.
It's excellent that a defense exists, but it's unfortunate that the web places the burden of opting-into it on the victim. There are certainly good reasons for two cross-origin windows to communicate (SSO, payments, etc), but documents engaging in those flows should opt-in to the communication channel, rather than asking all other origins to opt-out.
In the absence of an explicit header, documents' cross-origin opener policy should default to same-origin-allow-popups
, rather than unsafe-none
. This will have the effect of closing the window handle returned when a cross-origin document is opened via window.open
, and disowning the opener of the newly created window.
Those few services which intend to maintain that relationship when opened may easily do so by explicitly declaring a cross-origin opener policy of unsafe-none
. This will expose them to the risks discussed above, but they're choosing that risk intentionally in order to provide some service or obtain some information. They can reduce this risk by examining the incoming request, perhaps making decisions based upon request characteristics revealed in Fetch Metadata headers, the user's cookies, and so on.
The openee needs to set a header. That is, if https://a.example
opens https://b.example
in a new window, https://b.example
must send Cross-Origin-Opener-Policy: unsafe-none
in order to maintain status quo behavior.
Yes. This change would require cross-origin documents that wish to establish communication channels with their opener to be served securely. That gives the opener confidence that they're talking to the service they expect to be talking to, and not to the network at large.
In the example above, https://b.example
can opt into status quo behavior. http://b.example
could not do the same. b.example
's administrators would be well-advised to redirect their users to HTTPS if they'd like to keep the relationship running.
Note that http://a.example
would also be capable of establishing a communication channel with https://b.example
if the latter explicitly opted-in. This puts the incentive in the right place: services that intend to be used widely via popups (consider SSO, payment, etc) need to be delivered securely.
window.open
is used on ~2.6% of page views, which is a high-water mark for the impact. (Note that windows opened via <a target=_blank>
do not create an opener relationship today in Safari and Firefox, and Chromium is likely to follow suit.)
Before shipping this change, we obviously need to gather better metrics on the number of window.open calls that result in a handle that's used for cross-origin communication, either from the opener to the openee, or vice-versa. My intuition is that it's a small percentage of that 2.6%, concentrated in semi-centralized services that we have a chance of poking at successfully.
same-origin
is fairly strict, preventing pages from communicating with any cross-origin popup. This is a reasonable state for most applications to aim for, but it has the potential to break applications that expect to create popups that they can poke at. same-origin-allow-popups
is weaker from an isolation perspective, but much more likely to be deployable without application developers noticing negative impacts; it places the burden on the somewhat-centralized popped-up services, rather than the applications that wish to use them.
Two answers:
-
Cross-site nested documents are at reduced risk of attack, insofar as they aren't considered same-site.
SameSite=Lax
andSameSite=Strict
cookies won't be sent, unlike top-level navigation resulting fromwindow.open
. -
You're right, friendly interlocutor! We should default documents to asserting
Content-Security-Policy: frame-ancestors 'self'
in the absence of any otherframe-ancestors
orX-Frame-Options
orCross-Origin-Resource-Policy
declaration. What a wonderful idea that would coincidentally substantially reduce risk for browsers without OOPIF! But that's another document for another day...