However, we also want the web to be secure from attackers. Keeping users safe is paramount, which is why shared memory and high-resolution timers were effectively disabled at the start of 2018, in light of Spectre. Unfortunately, Spectre-attacks are made significantly more effective with high-resolution timers. And such timers can be created with shared memory. (This is accomplished by having one thread increment a shared memory location in a tight loop that another thread can sample as a nanosecond-resolution timer.)
Back to the drawing board
Fundamentally, for a Spectre attack to work, an attacker and victim need to reside in the same process. Like most applications on your computer, browsers used to use a single process. This would allow two open sites, say
victim.example, to Spectre-attack each other’s data as well as other data the browser might keep such as bookmarks or history. Browsers have long since become multi-process. With Chrome’s Site Isolation and Firefox’s Project Fission, browsers will isolate each site into its own process. This is possible due to the web platform’s retrofitted same-origin policy.
Unfortunately, isolating each site into its own process is still not sufficient for these reasons:
- The same-origin policy has a number of holes, two of which strongly informed our thinking during the design process:
attacker.example can fetch arbitrary
victim.example resources into
attacker.example’s process, e.g., through the
- Due to the existence of
document.domain, the minimal isolation boundary is a site (roughly the scheme and registrable domain of a website’s host) and not an origin (roughly a website’s scheme, host, and port).
- At this point, we don’t know if it’s feasible to isolate each site into its own process across all platforms. It is still a challenging endeavor on mobile. While possibly not a long-term problem, we would prefer a solution that allows reviving shared memory on mobile soon.
We need to address the issues above to revive shared memory and high-resolution timers. As such, we have been working on a system that meets the following requirements:
- It allows a website to process-isolate itself from attackers and thereby shield itself from intra-process high-resolution timer attacks.
- If a website wants to use these high-performance features, it also needs to process-isolate itself from victims. In particular, this means that it has to give up the ability to fetch arbitrary subresources from any site (e.g., through an
<img> element) because these end up in the same process. Instead, it can only fetch cross-origin resources from consenting origins.
- It allows a browser to run the entire website, including all of its frames and popups, in a single process. This is important to keep the web platform a consistent system across devices.
- It allows a browser to run each participating origin (i.e., not site) in its own process. This is the ideal end state across devices and it is important for the design to not prevent this.
- The system maintains backwards compatibility. We cannot ask billions of websites to rewrite their code.
Due to these requirements, the system must provide an opt-in mechanism. We cannot forbid websites from fetching cross-origin subresources, as this would not be backwards compatible. Sadly, restricting
document.domain is not backwards compatible either. More importantly, it would be unsafe to allow a website to embed cross-origin documents via an
<iframe> element and have those cross-origin resources end up in the same process without opting in.
Together with others in the WHATWG community, we designed a set of headers that meet these requirements.
Cross-Origin-Opener-Policy header allows you to process-isolate yourself from attackers. It also has the desirable effect that attackers cannot have access to your global object if they were to open you in a popup. This prevents XS-Leaks and various navigation attacks. Adopt this header even if you have no intention of using shared memory!
Cross-Origin-Embedder-Policy header with value
require-corp tells the browser to only allow this document to fetch cross-origin subresources from consenting websites. Technically, the way that this works is that those cross-origin resources need to specify the
Cross-Origin-Resource-Policy header with value
cross-origin to indicate consent.
Impact on documents
Cross-Origin-Opener Policy and
Cross-Origin-Embedder-Policy headers are set for a top-level document with the
require-corp values respectively, then:
- That document will be cross-origin isolated.
- Any descendant documents that also set
require-corp will be cross-origin isolated. (Not setting it results in a network error.)
- Any popups these documents open will either be cross-origin isolated or will not have a direct relationship with these documents. This is to say that there is no direct access through
window.opener or equivalent (i.e., it’s as if they were created using
performance.now() available. Evidently, it will not have access to a functional
The way these headers ensure mutual consent between origins gives browsers the freedom to put an entire website into a single process or put each of the origins into their own process, or something in between. While process-per-origin would be ideal, this is not always feasible on all devices. So having everything that is pulled into these one-or-more processes consent is a decent middle ground.
We created a safety backstop to be able to deal with novel cross-process attacks. And used an approach that avoids having to disable shared memory entirely to remain web compatible.
The result is Firefox’s
JSExecutionManager can be used to throttle CPU and power usage by background tabs. Using the
JSExecutionManager, we created a dynamic switch (
By default, this switch is off, but in the case of a novel cross-process attack, we could quickly flip it on. With this switch as a backstop, we can feel confident enabling shared memory in cross-origin isolated websites even when considering unlikely future worst-case scenarios.
Many thanks to Bas Schouten and Luke Wagner for their contributions to this post. And also, in no particular order, many thanks to Nika Layzell, Tom Tung, Valentin Gosu, Eden Chuang, Jens Manuel Stutte, Luke Wagner, Bas Schouten, Neha Kochar, Andrew Sutherland, Andrew Overholt, 蔡欣宜 (Hsin-Yi Tsai), Perry Jiang, Steve Fink, Mike Conca, Lars Thomas Hansen, Jeff Walden, Junior Hsu, Selena Deckelmann, and Eric Rescorla for their help getting this done in Firefox!
The post Safely reviving shared memory appeared first on Mozilla Hacks – the Web developer blog.
If you liked Safely reviving shared memory by Anne van Kesteren Then you'll love Web Design Agency Miami