Post-Spectre Web Development

Editor’s Draft,

This version:
https://w3c.github.io/webappsec-post-spectre-webdev/
Latest published version:
https://www.w3.org/TR/post-spectre-webdev/
Previous Versions:
Feedback:
public-webappsec@w3.org with subject line “[post-spectre-webdev] … message topic …” (archives)
Issue Tracking:
GitHub
Inline In Spec
Editor:
(Google)

Abstract

Post-Spectre, we need to adopt some new strategies for safe and secure web development. This document outlines a threat model we can share, and a set of mitigation recommendations.

TL;DR: Your data must not unexpectedly enter an attacker’s process.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Changes to this document may be tracked at https://github.com/w3c/webappsec.

The (archived) public mailing list public-webappsec@w3.org (see instructions) is preferred for discussion of this specification. When sending e-mail, please put the text “post-spectre-webdev” in the subject, preferably like this: “[post-spectre-webdev] …summary of comment…

This document was produced by the Web Application Security Working Group.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 15 September 2020 W3C Process Document.

1. Introduction

In early 2018, Spectre made it clear that a foundational security boundary the web aimed to maintain was substantially less robust than expected. [SPECTRE] This revelation has pushed web browsers to shift their focus from the platform-level origin boundary to an OS-level process boundary. Chromium’s threat model, for instance, now asserts that "active web content … will be able to read any and all data in the address space of the process that hosts it". [POST-SPECTRE-RETHINK] This shift in thinking imposes a shift in development practice, both for browser vendors, and for web developers. Browsers need to align the origin boundary with the process boundary through fundamental refactoring projects (for example, [SITE-ISOLATION] and [PROJECT-FISSION]). Moreover, browsers must provide web developers with tools to mitigate risk in the short term, and should push the platform towards safe default behaviors in the long term. The bad news is that this is going to be a lot of work, much of it falling on the shoulders of web developers. The good news is that a reasonable set of mitigation primitives exists today, ready and waiting for use.

This document will summarize the threat model which the Web Application Security Working Group espouses, point to a set of mitigations which seem promising, and provide concrete recommendations for developers responsible for protecting users' data.

1.1. Threat Model

Spectre-like side-channel attacks inexorably lead to a model in which active web content (JavaScript, WASM, probably CSS if we tried hard enough, and so on) can read any and all data which has entered the address space of the process which hosts it. While this has deep implications for user agent implementations' internal hardening strategies (stack canaries, ASLR, etc), here we’ll remain focused on the core implication at the web platform level, which is both simple and profound: any data which flows into a process hosting a given origin is legible to that origin. We must design accordingly.

In order to determine the scope of data that can be assumed accessible to an attacker, we must make a few assumptions about the normally-not-web-exposed process model which the user agent implements. The following seems like a good place to start:

  1. User agents are capable of separating the execution of a web origin’s code into a process distinct from the agent’s core. This separation enables the agent itself to access local devices, fetch resources, broker cross-process communication, and so on, in a way which remains invisible to any process potentially hosting untrusted code.

  2. User agents are able to make decisions about whether or not a given resource should be delivered to a process hosting a given origin based on characteristics of both the request and the response (headers, etc).

  3. User agents can consistently separate top-level, cross-origin windows into distinct processes. They cannot consistently separate same-site or same-origin windows into distinct processes given the potential for synchronous access between the windows.

  4. User agents cannot yet consistently separate framed origins into processes distinct from their embedders' origin.

    Note: Though some user agents support out-of-process frames [OOPIF], no agent supports it consistently across a broad range of devices and platforms. Ideally this will change over time, as the frame boundary must be one we can eventually consider robust.

With this in mind, our general assumption will be that an origin gains access to any resource which it renders (including images, stylesheets, scripts, frames, etc). Likewise, embedded frames gain access to their ancestors' content.

[COI-THREAT-MODEL] spells out more implications. Bring them in here for more nuance.

1.2. TL;DR

  1. Decide when (not!) to respond to requests by examining incoming headers, paying special attention to the Origin header on the one hand, and various Sec-Fetch- prefixed headers on the other, as described in [resource-isolation-policy].

  2. Restrict attackers' ability to load your data as a subresource by setting a cross-origin resource policy (CORP) of same-origin (opening up to same-site or cross-origin only when necessary).

  3. Restrict attackers' ability to frame your data as a document by opt-ing into framing protections via X-Frame-Options: SAMEORIGIN or CSP’s more granular frame-ancestors directive (frame-ancestors 'self' https://trusted.embedder, for example).

  4. Restrict attackers' ability to obtain a handle to your window by setting a cross-origin opener policy (COOP). In the best case, you can default to a restrictive same-origin value, opening up to same-origin-allow-popups or unsafe-none only if necessary.

  5. Prevent MIME-type confusion attacks and increase the robustness of passive defenses like cross-origin read blocking (CORB) / opaque response blocking ([ORB]) by setting correct Content-Type headers, and globally asserting X-Content-Type-Options: nosniff.

Describe these mitigations in more depth, swiping liberally from Notes on the threat model of cross-origin isolation, Safely reviving shared memory, etc.

2. Practical Examples

2.1. Subresources

Resources which are intended to be loaded into documents should protect themselves from being used in unexpected ways. Before walking through strategies for specific kinds of resources, a few headers seem generally applicable:

  1. Sites should use Fetch Metadata to make good decisions about when to serve resources, as described in [resource-isolation-policy]. In order to ensure that decision sticks, servers should explain its decision to the browser by sending a Vary header containing Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User. This ensures that the server has a chance to make different decisions for requests which will be used differently.

  2. Subresources should opt-out of MIME type sniffing by sending an X-Content-Type-Options header with a value of nosniff. This increases the robustness of MIME-based checks like cross-origin read blocking (CORB) / opaque response blocking ([ORB]), and mitigates some well-known risks around type confusion for scripts.

  3. Subresources are intended for inclusion in a given context, not as independently navigable documents. To mitigate the risk that navigation to a subresource causes script execution or opens an origin up to attack in some other way, servers can assert the following set of headers which collectively make it difficult to meaningfully abuse a subresource via navigation:

    • Using the Content-Security-Policy header’s to assert the sandbox directive ensures that these resources remain inactive if navigated to directly as a top-level document. No scripts will execute, and the resource will be pushed into an opaque origin.

      Note: Some servers deliver Content-Disposition: attachment; filename=file.name to obtain a similar effect. This was valuable to mitigate vulnerabilities in Flash, but the sandbox approach seems to more straightforwardly address the threats we care about today.

    • Asserting the Cross-Origin-Opener-Policy header with a value of same-origin prevents cross-origin documents from retaining a handle to the resource’s window if it’s opened in a popup.

    • The X-Frame-Options header with a value of DENY prevents the resource from being framed.

Most subresources, then, should contain the following block of headers, which you’ll see repeated a few times below:

Content-Security-Policy: sandbox
Cross-Origin-Opener-Policy: same-origin
Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

With these generic protections in mind, let’s sift through a few scenarios to determine what headers a server would be well-served to assert:

2.1.1. Static Subresources

By their nature, static resources contain the same data no matter who requests them, and therefore cannot contain interesting information that an attacker couldn’t otherwise obtain. There’s no risk to making these resources widely available, and value in allowing embedders to robustly debug, so something like the following response headers could be appropriate:

Access-Control-Allow-Origin: *
Cross-Origin-Resource-Policy: cross-origin
Timing-Allow-Origin: *
Content-Security-Policy: sandbox
Cross-Origin-Opener-Policy: same-origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

Note: Purely static resources always respond with the same data, no matter the request. There’s therefore little benefit to sending a Vary header: it can be safely omitted for these responses.

CDNs are the canonical static resource distribution points, and many use the pattern above. Take a look at the following common resources' response headers for inspiration:

Similarly, application-specific static resource servers are a good place to look for this practice. Consider:

2.1.2. Dynamic Subresources

Subresources that contain data personalized to a given user are juicy targets for attackers, and must be defended by ensuring that they’re loaded only in ways that are appropriate for the data in question. A few cases are well worth considering:

  1. Application-internal resources (private API endpoints, avatar images, uploaded data, etc.) should not be available to any cross-origin requestor. These resources should be restricted to usage as a subresource in same-origin contexts by sending a Cross-Origin-Resource-Policy header with a value of same-origin:

    Cross-Origin-Resource-Policy: same-origin
    Content-Security-Policy: sandbox
    Cross-Origin-Opener-Policy: same-origin
    Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User
    X-Content-Type-Options: nosniff
    X-Frame-Options: DENY
    

    This header will prevent cross-origin attackers from loading the resource as a response to a no-cors request.

    For example, examine the headers returned when requesting endpoints like the following:

  2. Personalized resources intended for cross-origin use (public API endpoints, etc) should carefully consider incoming requests' properties before responding. These endpoints can only safely be enabled by requiring CORS, and choosing the set of origins for which a given response can be exposed by setting the appropriate access-control headers, for example:

    Access-Control-Allow-Credentials: true
    Access-Control-Allow-Origin: https://trusted.example
    Access-Control-Allow-Methods: POST
    Access-Control-Allow-Headers: ...
    Access-Control-Allow-...: ...
    Cross-Origin-Resource-Policy: same-origin
    Content-Security-Policy: sandbox
    Cross-Origin-Opener-Policy: same-origin
    Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User
    X-Content-Type-Options: nosniff
    X-Frame-Options: DENY
    

    Note: The Cross-Origin-Resource-Policy header is only processed for requests that are _not_ using CORS for access control ("no-cors requests"). Sending Cross-Origin-Resource-Policy: same-origin is therefore not harmful, and works to ensure that no-cors usage isn’t accidentally allowed.

    For example, examine the headers returned when requesting endpoints like the following:

  3. Personalized resources that are intended for cross-origin no-cors embedding, but which don’t intend to be directly legible in that context (avatar images, authenticated media, etc). These should enable cross-origin embedding via Cross-Origin-Resource-Policy, but _not_ via CORS access control headers:

    Cross-Origin-Resource-Policy: cross-origin
    Content-Security-Policy: sandbox
    Cross-Origin-Opener-Policy: same-origin
    Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User
    X-Content-Type-Options: nosniff
    X-Frame-Options: DENY
    
    Note: That this allows the resource to be used by any cross-origin document. That’s reasonable for some use cases, but requiring CORS, and opting-in a small set of origins via appropriate access-control headers is a possible alternative for some resources. This approach will give those contexts trivial access to the resource’s bits, so the granularity is a tradeoff. Still, considering this case to be the same as the "personalized resources intended for cross-origin use" isn’t unreasonable.

    If we implemented more granular bindings for CORP headers (along the lines of Cross-Origin-Resource-Policy: https://trusted.example), we could avoid this tradeoff entirely. <https://github.com/whatwg/fetch/issues/760>

    For example:

2.2. Documents

2.2.1. Fully-Isolated Documents

Documents that require users to be signed-in almost certainly contain information that shouldn’t be revealed to attackers. These pages should take care to isolate themselves from other origins, both by making _a priori_ decisions about whether to serve the page at all (see [resource-isolation-policy], for example), and by giving clients careful instructions about how the page can be used once delivered. For instance, something like the following set of response headers could be appropriate:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN

Note: Documents which need to make use of APIs that require full cross-origin isolation (such as SharedArrayBuffer), will also need to serve a Cross-Origin-Embedder-Policy header, as outlined in [coop-coep] and [cross-origin-isolation-guide].

Account settings pages, admin panels, and application-specific documents are all good examples of resources which would benefit from as much isolation as possible. For real-life examples, consider:

2.2.2. Documents Expecting to Open Cross-Origin Windows

Not every document that requires sign-in can be fully-isolated from the rest of the internet. It’s often the case that partial isolation is a better fit. Consider sites that depend upon cross-origin windows for federated workflows involving payments or sign-in, for example. These pages would generally benefit from restricting attackers' ability to embed them, or obtain their window handle, but they can’t easily lock themselves off from all such vectors via Cross-Origin-Opener-Policy: same-origin and X-Frame-Options: DENY. In these cases, something like the following set of response headers might be appropriate:

Cross-Origin-Opener-Policy: same-origin-allow-popups
Cross-Origin-Resource-Policy: same-origin
Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN

The only difference between this case and the "Fully-Isolated" case above is the Cross-Origin-Opener-Policy value. same-origin will break the opener relationship between the document and any cross-origin window, regardless of who opened whom. same-origin-allow-popups will break cross-origin opener relationships initiated by a cross-origin document’s use of window.open(), but will allow the asserting document to open cross-origin windows that retain an opener relationship.

2.2.3. Documents Expecting Cross-Origin Openers

Federated sign-in forms and payment providers are clear examples of documents which intend to be opened by cross-origin windows, and require that relationship to be maintained in order to facilitate communication via channels like postMessage(message, options) or navigation. These documents cannot isolate themselves completely, but can prevent themselves from being embedded or fetched cross-origin. Three scenarios are worth considering:

  1. Documents that only wish to be opened in cross-origin popups could loosen their cross-origin opener policy by serving the following headers:

    Cross-Origin-Resource-Policy: same-origin
    Cross-Origin-Opener-Policy: unsafe-none
    Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User
    X-Content-Type-Options: nosniff
    X-Frame-Options: SAMEORIGIN
    

    For example:

    • Find some links.

  2. Documents that only wish to be framed in cross-origin contexts could loosen their framing protections by serving the following headers:

    Cross-Origin-Resource-Policy: same-origin
    Cross-Origin-Opener-Policy: same-origin
    Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User
    X-Content-Type-Options: nosniff
    X-Frame-Options: ALLOWALL
    
    Note: That this allows embedding by any cross-origin documents. That’s reasonable for some widgety use cases, but when possible, a more secure alternative would specify a list of origins which are allowed to embed the document via the frame-ancestors CSP directive. That is, in addition to the X-Frame-Options header above, the following header could also be included to restrict the document to a short list of trusted embedders:
    Content-Security-Policy: frame-ancestors https://trusted1.example https://trusted2.example
    

    For example:

    • Find some links.

  3. Documents that support both popup and framing scenarios need to loosen both their cross-origin opener policies and framing protections by combining the recommendations above, serving the following headers:

    Cross-Origin-Resource-Policy: same-origin
    Cross-Origin-Opener-Policy: unsafe-none
    Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site, Sec-Fetch-User
    X-Content-Type-Options: nosniff
    X-Frame-Options: ALLOWALL
    

    For example:

    • Find some links.

3. Implementation Considerations

3.1. Explicitly Setting Headers with Default Values

Several recommendations above suggest that developers would be well-served to set headers like X-Frame-Options: ALLOWALL or Cross-Origin-Opener-Policy: unsafe-none on responses. These map to the web’s status quo behavior, and seem therefore superfluous. Why should developers set them?

The core reason is that these defaults are poor fits for today’s threats, and we ought to be working to change them. Proposals like [EMBEDDING-REQUIRES-OPT-IN] and [COOP-BY-DEFAULT] suggest that we shift the web’s defaults away from requiring developers to opt-into more secure behaviors by making them opt-out rather than opt-in. This would place the configuration cost on those developers whose projects require risky settings.

This document recommends setting those less-secure header values explicitly, as that makes it more likely that we’ll be able to shift the web’s defaults in the future.

4. Acknowledgements

This document relies upon a number of excellent resources that spell out much of the foundation of our understanding of Spectre’s implications for the web, and justify the mitigation strategies we currently espouse. The following is an incomplete list of those works:

[APPLICATION-PRINCIPALS], [LONG-TERM-MITIGATIONS], [SPECTRE-SHAPED-WEB], [POST-SPECTRE-RETHINK], [SPILLING-THE-BEANS], [CROSS-ORIGIN-EMBEDDER-POLICY], [CROSS-ORIGIN-OPENER-POLICY-EXPLAINER], [COOP-COEP-EXPLAINED], [SAFELY-REVIVING-SHARED-MEMORY], [COI-THREAT-MODEL]

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by reference

References

Normative References

[CSP3]
Mike West. Content Security Policy Level 3. 13 March 2021. WD. URL: https://www.w3.org/TR/CSP3/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[RFC7231]
R. Fielding, Ed.; J. Reschke, Ed.. Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content. June 2014. Proposed Standard. URL: https://httpwg.org/specs/rfc7231.html

Informative References

[APPLICATION-PRINCIPALS]
Chris Palmer. Isolating Application-Defined Principals. 2018-06-19. URL: https://noncombatant.org/application-principals/
[COI-THREAT-MODEL]
Artur Janc. Notes on the threat model of cross-origin isolation. 2020-12. URL: https://arturjanc.com/coi-threat-model.pdf
[COOP-BY-DEFAULT]
Mike West. COOP By Default. URL: https://github.com/mikewest/coop-by-default
[COOP-COEP]
Eiji Kitamura. Making your website 'cross-origin isolated' using COOP and COEP. URL: https://web.dev/coop-coep/
[COOP-COEP-EXPLAINED]
Artur Janc; Charlie Reis; Anne van Kesteren. COOP and COEP Explained. 2020-01-03. URL: https://docs.google.com/document/d/1zDlfvfTJ_9e8Jdc8ehuV4zMEu9ySMCiTGMS9y0GU92k/edit
[CROSS-ORIGIN-EMBEDDER-POLICY]
Mike West. Cross-Origin Embedder Policy. 2020-09-29. URL: https://wicg.github.io/cross-origin-embedder-policy/
[CROSS-ORIGIN-ISOLATION-GUIDE]
Eiji Kitamura. A guide to enable cross-origin isolation. URL: https://web.dev/cross-origin-isolation-guide/
[CROSS-ORIGIN-OPENER-POLICY-EXPLAINER]
Charlie Reis; Camille Lamy. Cross-Origin-Opener-Policy Explainer. 2020-05-24. URL: https://docs.google.com/document/d/1Ey3MXcLzwR1T7aarkpBXEwP7jKdd2NvQdgYvF8_8scI/edit
[EMBEDDING-REQUIRES-OPT-IN]
Mike West. Embedding Should Require Explicit Opt-In. URL: https://github.com/mikewest/embedding-requires-opt-in
[LONG-TERM-MITIGATIONS]
Charlie Reis. Long-Term Web Browser Mitigations for Spectre. 2019-03-04. URL: https://docs.google.com/document/d/1dnUjxfGWnvhQEIyCZb0F2LmCZ9gio6ogu2rhMGqi6gY/edit
[OOPIF]
Chromium. Out-of-Process iframes (OOPIFs). URL: https://www.chromium.org/developers/design-documents/oop-iframes
[ORB]
Anne van Kesteren. Opaque Response Blocking (ORB, aka CORB++). URL: https://github.com/annevk/orb
[POST-SPECTRE-RETHINK]
Chromium. Post-Spectre Threat Model Re-Think. URL: https://chromium.googlesource.com/chromium/src/+/master/docs/security/side-channel-threat-model.md
[PROJECT-FISSION]
Mozilla. Project Fission. URL: https://wiki.mozilla.org/Project_Fission
[RESOURCE-ISOLATION-POLICY]
XSLeaks Wiki. Resource Isolation Policy. URL: https://xsleaks.dev/docs/defenses/isolation-policies/resource-isolation/
[SAFELY-REVIVING-SHARED-MEMORY]
Anne van Kesteren. Safely reviving shared memory. 2020-07-21. URL: https://hacks.mozilla.org/2020/07/safely-reviving-shared-memory/
[SITE-ISOLATION]
Chromium. Site Isolation. URL: https://www.chromium.org/Home/chromium-security/site-isolation
[SPECTRE]
Paul Kocher; et al. Spectre Attacks: Exploiting Speculative Execution. May 2019. URL: https://ieeexplore.ieee.org/document/8835233
[SPECTRE-SHAPED-WEB]
Anne van Kesteren. A Spectre-shaped Web. 2019-04-16. URL: https://docs.google.com/presentation/d/1sadl7jTrBIECCanuqSrNndnDr82NGW1yyuXFT1Dc7SQ/edit#slide=id.p
[SPILLING-THE-BEANS]
Artur Janc; Mike West. How do we Stop Spilling The Beans Across Origins?. 2018-05-16. URL: https://www.arturjanc.com/cross-origin-infoleaks.pdf

Issues Index

[COI-THREAT-MODEL] spells out more implications. Bring them in here for more nuance.
Describe these mitigations in more depth, swiping liberally from Notes on the threat model of cross-origin isolation, Safely reviving shared memory, etc.
If we implemented more granular bindings for CORP headers (along the lines of Cross-Origin-Resource-Policy: https://trusted.example), we could avoid this tradeoff entirely. <https://github.com/whatwg/fetch/issues/760>
Find some links.
Find some links.
Find some links.