blob: a36305ca7aa43c47c9ddf3a1b8db7f34c2b2e7b3 [file] [log] [blame] [view]
Mike Westee1bf4362021-02-26 22:07:061# Post-Spectre Web Development
2
3Contributors: Artur Janc, Camille Lamy, Charlie Reis, Jun Kokatsu, Mike West.
4Patches and corrections welcome!
5
Mike Westc02a00982021-03-12 13:24:516Last Updated: Mar. 12th, 2021
7
8*** note
9**Note**: This document has been [adopted][cfc] by the W3C's Web Application Security
10Working Group; [https://w3c.github.io/webappsec-post-spectre-webdev/][ED] is
11proceeding under that group's aegis, and this document will redirect there
12once the working group is satisfied with its recommendations.
13***
14
15[cfc]: https://lists.w3.org/Archives/Public/public-webappsec/2021Feb/0007.html
16[ED]: https://w3c.github.io/webappsec-post-spectre-webdev/
Mike Westee1bf4362021-02-26 22:07:0617
18[TOC]
19
20## Introduction
21
22In early 2018, [Spectre][spectre] made it clear that a foundational security
23boundary the web aimed to maintain was substantially less robust than expected.
24This revelation has pushed web browsers to shift their focus from the
25platform-level origin boundary to an OS-level process boundary. Chromium's
26[threat model][post-spectre-rethink], for instance, now asserts that "active
27web content … will be able to read any and all data in the address space of the
28process that hosts it". This shift in thinking imposes a shift in development
29practice, both for browser vendors, and for web developers. Browsers need to
30align the origin boundary with the process boundary through fundamental
31refactoring projects (for example, Chromium's [Site Isolation][site-isolation],
32and Firefox's [Project Fission][project-fission]). Moreover, browsers must
33provide web developers with tools to mitigate risk in the short term, and should
34push the platform towards safe default behaviors in the long term. The bad news
35is that this is going to be a lot of work, much of it falling on the shoulders
36of web developers. The good news is that a reasonable set of mitigation
37primitives exists today, ready and waiting for use.
38
39This document will point to a set of mitigations which seem promising, and
40provide concrete recommendations for web developers responsible for protecting
41users' data.
42
43[spectre]: https://spectreattack.com/
John Palmer046f9872021-05-24 01:24:5644[post-spectre-rethink]: https://chromium.googlesource.com/chromium/src/+/main/docs/security/side-channel-threat-model.md
Mike Westee1bf4362021-02-26 22:07:0645[site-isolation]: https://www.chromium.org/Home/chromium-security/site-isolation
46[project-fission]: https://wiki.mozilla.org/Project_Fission
47
48
49### Threat Model
50
51Spectre-like side-channel attacks inexorably lead to a model in which active web
52content (JavaScript, WASM, probably CSS if we tried hard enough, and so on) can
53read any and all data which has entered the address space of the process which
54hosts it. While this has deep implications for user agent implementations'
55internal hardening strategies (stack canaries, ASLR, etc), here we'll remain
56focused on the core implication at the web platform level, which is both simple
57and profound: any data which flows into a process hosting a given origin is
58legible to that origin. We must design accordingly.
59
60In order to determine the scope of data that can be assumed accessible to an
61attacker, we must make a few assumptions about the normally-not-web-exposed
62process model which the user agent implements. The following seems like a good
63place to start:
64
651. User agents are capable of separating the execution of a web origin's code
66 into a process distinct from the agent's core. This separation enables the
67 agent itself to access local devices, fetch resources, broker cross-process
68 communication, and so on, in a way which remains invisible to any process
69 potentially hosting untrusted code.
70
712. User agents are able to make decisions about whether or not a given resource
72 should be delivered to a process hosting a given origin based on
73 characteristics of both the request and the response (headers, etc).
74
753. User agents can consistently separate top-level, cross-origin windows into
76 distinct processes. They cannot consistently separate same-site or
77 same-origin windows into distinct processes given the potential for
78 synchronous access between the windows.
79
804. User agents cannot yet consistently separate framed origins into processes
81 distinct from their embedders' origin.
82
83 Note: Though some user agents support [out-of-process frames][oopif], no
84 agent supports it consistently across a broad range of devices and
85 platforms. Ideally this will change over time, as the frame boundary *must*
86 be one we can eventually consider robust.
87
88With this in mind, our general assumption will be that an origin gains access to
89any resource which it renders (including images, stylesheets, scripts, frames,
90etc). Likewise, embedded frames gain access to their ancestors' content.
91
92This model is spelled out in more detail in both Chromium's
93[Post-Spectre Threat Model Rethink][post-spectre-rethink], and in Artur Janc's
94[Notes on the threat model of cross-origin isolation][coi-threat-model].
95
96[oopif]: https://www.chromium.org/developers/design-documents/oop-iframes
97[coi-threat-model]: https://arturjanc.com/coi-threat-model.pdf
98
99
100### Mitigation TL;DR
101
1021. **Decide when (not!) to respond to requests** by examining incoming headers,
103 paying special attention to the `Origin` header on the one hand, and various
104 `Sec-Fetch-` prefixed headers on the other, as described in the article
105 [Protect your resources from web attacks with Fetch Metadata][fetch-metadata].
106
1072. **Restrict attackers' ability to load your data as a subresource** by
108 setting a [cross-origin resource policy][corp] (CORP) of `same-origin`
109 (opening up to `same-site` or `cross-origin` only when necessary).
110
1113. **Restrict attackers' ability to frame your data as a document** by opt-ing
112 into framing protections via `X-Frame-Options: SAMEORIGIN` or CSP's more
113 granular `frame-ancestors` directive (`frame-ancestors 'self'
114 https://trusted.embedder`, for example).
115
1164. **Restrict attackers' ability to obtain a handle to your window** by setting
117 a [cross-origin opener policy][coop] (COOP). In the best case, you can
118 default to a restrictive `same-origin` value, opening up to
119 `same-origin-allow-popups` or `unsafe-none` only if necessary.
120
1215. **Prevent MIME-type confusion attacks** and increase the robustness of
122 passive defenses like [cross-origin read blocking][corb] (CORB) /
123 [opaque response blocking][orb] (ORB) by setting correct `Content-Type`
124 headers, and globally asserting `X-Content-Type-Options: nosniff`.
125
126[fetch-metadata]: https://web.dev/fetch-metadata/
127[corp]: https://resourcepolicy.fyi/
128[coop]: https://docs.google.com/document/d/1Ey3MXcLzwR1T7aarkpBXEwP7jKdd2NvQdgYvF8_8scI/edit
John Palmer046f9872021-05-24 01:24:56129[corb]: https://chromium.googlesource.com/chromium/src/+/main/services/network/cross_origin_read_blocking_explainer.md
Mike Westee1bf4362021-02-26 22:07:06130[orb]: https://github.com/annevk/orb
131
132
133## Practical Examples
134
135### Subresources
136
137Resources which are intended to be loaded into documents should protect
138themselves from being used in unexpected ways. Before walking through strategies
139for specific kinds of resources, a few headers seem generally applicable:
140
1411. Sites should use Fetch Metadata to make good decisions about
142 [when to serve resources][fetch-metadata]. In order to ensure that decision
143 sticks, servers should explain its decision to the browser by sending a
144 `Vary` header containing `Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site`.
145 This ensures that the server has a chance to make different decisions for
146 requests which will be *used* differently.
147
1482. Subresources should opt-out of MIME type sniffing by sending an
149 `X-Content-Type-Options` header with a value of `nosniff`. This increases
150 the robustness of MIME-based checks like [cross-origin read blocking][corb]
151 and [opaque response blocking][orb], and mitigates some well-known risks
152 around type confusion for scripts.
153
1543. Subresources are intended for inclusion in a given context, not as
155 independently navigable documents. To mitigate the risk that navigation to a
156 subresource causes script execution or opens an origin up to attack in some
157 other way, servers can assert the following set of headers which
158 collectively make it difficult to meaningfully abuse a subresource via
159 navigation:
160
161 * Use the `Content-Security-Policy` header to assert the `sandbox`
162 directive. This ensures that these resources remain inactive if
163 navigated to directly as a top-level document. No scripts will execute,
164 and the resource will be pushed into an "opaque" origin.
165
166 Note: Some servers deliver `Content-Disposition: attachment;
167 filename=file.name` to obtain a similar effect. This was valuable to
168 mitigate vulnerabilies in Flash, but the `sandbox` approach seems to
169 more straightforwardly address the threats we care about today.
170
171 * Asserting the [`Cross-Origin-Opener-Policy`][coop] header with a value
172 of `same-origin` prevents cross-origin documents from retaining a handle
173 to the resource's window if it's opened in a popup.
174
175 * Sending the `X-Frame-Options` header with a value of `DENY` prevents the
176 resource from being framed.
177
178Most subresources, then, should contain the following block of headers, which
179you'll see repeated a few times below:
180
181```http
182Content-Security-Policy: sandbox
183Cross-Origin-Opener-Policy: same-origin
184Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
185X-Content-Type-Options: nosniff
186X-Frame-Options: DENY
187```
188
189With these generic protections in mind, let's sift through a few scenarios to
190determine what headers a server would be well-served to assert:
191
192#### Static Subresources
193
194By their nature, static resources contain the same data no matter who requests
195them, and therefore cannot contain interesting information that an attacker
196couldn't otherwise obtain. There's no risk to making these resources widely
197available, and value in allowing embedders to robustly debug, so something like
198the following response headers could be appropriate:
199
200```http
201Access-Control-Allow-Origin: *
202Cross-Origin-Resource-Policy: cross-origin
203Timing-Allow-Origin: *
204Content-Security-Policy: sandbox
205Cross-Origin-Opener-Policy: same-origin
206Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
207X-Content-Type-Options: nosniff
208X-Frame-Options: DENY
209```
210
211CDNs are the canonical static resource distribution points, and many use the
212pattern above. Take a look at the following common resources' response headers
213for inspiration:
214
215* <a href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js">`https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js`</a>
216* <a href="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js">`https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js`</a>
217* <a href="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js">`https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js`</a>
218* <a href="https://ssl.google-analytics.com/ga.js">`https://ssl.google-analytics.com/ga.js`</a>
219
220Similarly, application-specific static resource servers are a good place to look
221for this practice. Consider:
222
223* <a href="https://static.xx.fbcdn.net/rsrc.php/v3/y2/r/zVvRrO8pOtu.png">`https://static.xx.fbcdn.net/rsrc.php/v3/y2/r/zVvRrO8pOtu.png`</a>
224* <a href="https://www.gstatic.com/images/branding/googlelogo/svg/googlelogo_clr_74x24px.svg">`https://www.gstatic.com/images/branding/googlelogo/svg/googlelogo_clr_74x24px.svg`</a>
225
226#### Dynamic Subresources
227
228Subresources that contain data personalized to a given user are juicy targets
229for attackers, and must be defended by ensuring that they're loaded only in
230ways that are appropriate for the data in question. A few cases are well worth
231considering:
232
2331. Application-internal resources (private API endpoints, avatar images,
234 uploaded data, etc.) should not be available to any cross-origin requestor.
235 These resources should be restricted to usage as a subresource in
236 same-origin contexts by sending a [`Cross-Origin-Resource-Policy`][corp]
237 header with a value of `same-origin`:
238
239 ```http
240 Cross-Origin-Resource-Policy: same-origin
241 Content-Security-Policy: sandbox
242 Cross-Origin-Opener-Policy: same-origin
243 Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
244 X-Content-Type-Options: nosniff
245 X-Frame-Options: DENY
246 ```
247
248 This header will prevent cross-origin attackers from loading the resource as
249 a response to a `no-cors` request.
250
251 For example, examine the headers returned when requesting endpoints like the
252 following:
253
254 * <a href="https://myaccount.google.com/_/AccountSettingsUi/browserinfo">`https://myaccount.google.com/_/AccountSettingsUi/browserinfo`</a>
255 * <a href="https://twitter.com/i/api/1.1/branch/init.json">`https://twitter.com/i/api/1.1/branch/init.json`</a>
256 * <a href="https://www.facebook.com/ajax/webstorage/process_keys/?state=0">`https://www.facebook.com/ajax/webstorage/process_keys/?state=0`</a>
257
2582. Personalized resources intended for cross-origin use (public API endpoints,
259 etc) should carefully consider incoming requests' properties before
260 responding. These endpoints can only safely be enabled by requiring CORS,
261 and choosing the set of origins for which a given response can be exposed by
262 setting the appropriate access-control headers, for example:
263
264 ```http
265 Access-Control-Allow-Credentials: true
266 Access-Control-Allow-Origin: https://trusted.example
267 Access-Control-Allow-Methods: POST
268 Access-Control-Allow-Headers: ...
269 Access-Control-Allow-...: ...
270 Cross-Origin-Resource-Policy: same-origin
271 Content-Security-Policy: sandbox
272 Cross-Origin-Opener-Policy: same-origin
273 Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
274 X-Content-Type-Options: nosniff
275 X-Frame-Options: DENY
276 ```
277
278 Note: The `Cross-Origin-Resource-Policy` header is only processed for
279 requests that are _not_ using CORS for access control ("`no-cors`
280 requests"). Sending `Cross-Origin-Resource-Policy: same-origin` is
281 therefore not harmful, and works to ensure that `no-cors` usage isn't
282 accidentally allowed.
283
284 For example, examine the headers returned when requesting endpoints like
285 the following:
286
287 * <a href="https://api.twitter.com/1.1/jot/client_event.json">`https://api.twitter.com/1.1/jot/client_event.json`</a>
288 * <a href="https://play.google.com/log?format=json&hasfast=true">`https://play.google.com/log?format=json&hasfast=true`</a>
289 * <a href="https://securepubads.g.doubleclick.net/pcs/view">`https://securepubads.g.doubleclick.net/pcs/view`</a>
290 * <a href="https://c.amazon-adsystem.com/e/dtb/bid">`https://c.amazon-adsystem.com/e/dtb/bid`</a>
291
2923. Personalized resources that are intended for cross-origin `no-cors`
293 embedding, but which don't intend to be directly legible in that context
294 (avatar images, authenticated media, etc). These should enable cross-origin
295 embedding via `Cross-Origin-Resource-Policy`, but _not_ via CORS access
296 control headers:
297
298 ```http
299 Cross-Origin-Resource-Policy: cross-origin
300 Content-Security-Policy: sandbox
301 Cross-Origin-Opener-Policy: same-origin
302 Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
303 X-Content-Type-Options: nosniff
304 X-Frame-Options: DENY
305 ```
306
307 Note, that this allows the resource to be used by any cross-origin document.
308 That's reasonable for some use cases, but requiring CORS, and opting-in a
309 small set of origins via appropriate access-control headers is a possible
310 alternative for some resources. This approach will give those contexts
311 trivial access to the resource's bits, so the granularity is a tradeoff.
312 Still, considering this case to be the same as the "personalized resources
313 intended for cross-origin use" isn't unreasonable.
314
315 If we implemented more granular bindings for CORP headers (along the lines
316 of `Cross-Origin-Resource-Policy: https://trusted.example`), we could avoid
317 this tradeoff entirely. That's proposed in
318 [whatwg/fetch#760](https://github.com/whatwg/fetch/issues/670).
319
320
321 For example:
322
323 * <a href="https://lh3.google.com/u/0/d/1JBUaX1xSOZRxBk5bRNZWgnzyJoCQC52TIRokACBSmGc=w512">`https://lh3.google.com/u/0/d/1JBUaX1xSOZRxBk5bRNZWgnzyJoCQC52TIRokACBSmGc=w512`</a>
324
325
326### Documents {#documents}
327
328#### Fully-Isolated Documents
329
330Documents that require users to be signed-in almost certainly contain
331information that shouldn't be revealed to attackers. These pages should take
332care to isolate themselves from other origins, both by making _a priori_
333decisions about [whether to serve the page at all][fetch-metadata], and by
334giving clients careful instructions about how the page can be used once
335delivered. For instance, something like the following set of response headers
336could be appropriate:
337
338```http
339Cross-Origin-Opener-Policy: same-origin
340Cross-Origin-Resource-Policy: same-origin
341Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
342X-Content-Type-Options: nosniff
343X-Frame-Options: SAMEORIGIN
344```
345
346Note: Documents which need to make use of APIs that require full cross-origin
347isolation (such as `SharedArrayBuffer`), will also need to serve a
348[`Cross-Origin-Embedder-Policy`][coep] header, as outlined in
349[Making your website "cross-origin isolated" using COOP and COEP][coop-coep].
350
351Account settings pages, admin panels, and application-specific documents are all
352good examples of resources which would benefit from as much isolation as
353possible. For real-life examples, consider:
354
355* <a href="https://myaccount.google.com/">`https://myaccount.google.com/`</a>
356
357[coep]: https://wicg.github.io/cross-origin-embedder-policy/
358[coop-coep]: https://web.dev/coop-coep/
359
360
361#### Documents Expecting to Open Cross-Origin Windows
362
363Not every document that requires sign-in can be fully-isolated from the rest of
364the internet. It's often the case that partial isolation is a better fit.
365Consider sites that depend upon cross-origin windows for federated workflows
366involving payments or sign-in, for example. These pages would generally benefit
367from restricting attackers' ability to embed them, or obtain their window
368handle, but they can't easily lock themselves off from all such vectors via
369`Cross-Origin-Opener-Policy: same-origin` and `X-Frame-Options: DENY`. In these
370cases, something like the following set of response headers might be
371appropriate:
372
373```http
374Cross-Origin-Opener-Policy: same-origin-allow-popups
375Cross-Origin-Resource-Policy: same-origin
376Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
377X-Content-Type-Options: nosniff
378X-Frame-Options: SAMEORIGIN
379```
380
381The only difference between this case and the "Fully-Isolated" case above is the
382`Cross-Origin-Opener-Policy` value. `same-origin` will break the opener
383relationship between the document and any cross-origin window, regardless of who
384opened whom. `same-origin-allow-popups` will break cross-origin opener
385relationships initiated by a cross-origin document's use of `window.open()`, but
386will allow the asserting document to open cross-origin windows that retain an
387opener relationship.
388
389
390#### Documents Expecting Cross-Origin Openers
391
392Federated sign-in forms and payment providers are clear examples of documents
393which intend to be opened by cross-origin windows, and require that relationship
394to be maintained in order to facilitate communication via channels like
395`postMessage()` or navigation. These documents cannot isolate themselves
396completely, but can prevent themselves from being embedded or fetched
397cross-origin. Three scenarios are worth considering:
398
3991. Documents that only wish to be opened in cross-origin popups could loosen
400 their cross-origin opener policy by serving the following headers:
401
402 ```http
403 Cross-Origin-Resource-Policy: same-origin
404 Cross-Origin-Opener-Policy: unsafe-none
405 Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
406 X-Content-Type-Options: nosniff
407 X-Frame-Options: SAMEORIGIN
408 ```
409
410 For example:
411
412 * > TODO: Find some links.
413
4142. Documents that only wish to be framed in cross-origin contexts could loosen
415 their framing protections by serving the following headers:
416
417 ```http
418 Cross-Origin-Resource-Policy: same-origin
419 Cross-Origin-Opener-Policy: same-origin
420 Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
421 X-Content-Type-Options: nosniff
422 X-Frame-Options: ALLOWALL
423 ```
424
425
426 Note that this allows embedding by any cross-origin documents. That's
427 reasonable for some widgety use cases, but when possible, a more secure
428 alternative would specify a list of origins which are allowed to embed the
429 document via the `frame-ancestors` CSP directive. That is, in addition to
430 the `X-Frame-Options` header above, the following header could also be
431 included to restrict the document to a short list of trusted embedders:
432
433 ```http
434 Content-Security-Policy: frame-ancestors https://trusted1.example https://trusted2.example
435 ```
436
437 For example:
438
439 * > TODO: Find some links.
440
4413. Documents that support both popup and framing scenarios need to loosen both
442 their cross-origin opener policies and framing protections by combining the
443 recommendations above, serving the following headers:
444
445 ```http
446 Cross-Origin-Resource-Policy: same-origin
447 Cross-Origin-Opener-Policy: unsafe-none
448 Vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
449 X-Content-Type-Options: nosniff
450 X-Frame-Options: ALLOWALL
451 ```
452
453 For example:
454
455 * > TODO: Find some links.
456
457## Implementation Considerations
458
459### Explicitly Setting Headers with Default Values
460
461Several recommendations above suggest that developers would be well-served to
462set headers like `X-Frame-Options: ALLOWALL` or `Cross-Origin-Opener-Policy:
463unsafe-none` on responses. These map to the web's status quo behavior, and seem
464therefore superfluous. Why should developers set them?
465
466The core reason is that these defaults are poor fits for today's threats, and we
467ought to be working to change them. Proposals like [Embedding Requires Opt-In][embedding]
468and [COOP By Default][coop-by-default] suggest that we should shift the web's
469defaults away from requiring developers to opt-into more secure behaviors by
470making them opt-out rather than opt-in. This would place the configuration cost
471on those developers whose projects require risky settings.
472
473This document recommends setting those less-secure header values explicitly, as
474that makes it more likely that we'll be able to shift the web's defaults in the
475future.
476
477[embedding]: https://github.com/mikewest/embedding-requires-opt-in
478[coop-by-default]: https://github.com/mikewest/coop-by-default
479
480## Acknowledgements
481
482This document relies upon a number of excellent resources that spell out much of
483the foundation of our understanding of Spectre's implications for the web, and
484justify the mitigation strategies we currently espouse. The following is an
485incomplete list of those works, in no particular order:
486
487* Chris Palmer's
488 [Isolating Application-Defined Principles](https://noncombatant.org/application-principals/)
489
490* Charlie Reis'
491 [Long-Term Web Browser Mitigations for Spectre](https://docs.google.com/document/d/1dnUjxfGWnvhQEIyCZb0F2LmCZ9gio6ogu2rhMGqi6gY/edit)
492
493* Anne van Kesteren's
494 [A Spectre-Shaped Web](https://docs.google.com/presentation/d/1sadl7jTrBIECCanuqSrNndnDr82NGW1yyuXFT1Dc7SQ/edit#slide=id.p),
495 [Safely Reviving Shared Memory](https://hacks.mozilla.org/2020/07/safely-reviving-shared-memory/),
496 and [Opaque Resource Blocking](https://github.com/annevk/orb).
497
498* Artur Janc and Mike West's [How do we stop spilling the beans across origins?](https://www.arturjanc.com/cross-origin-infoleaks.pdf)
499
500* Mike West's [Cross-Origin Embedder Policy explainer](https://wicg.github.io/cross-origin-embedder-policy/)
501
502* Charlie Reis and Camille Lamy's [Cross-Origin Opener Policy explainer](https://docs.google.com/document/d/1Ey3MXcLzwR1T7aarkpBXEwP7jKdd2NvQdgYvF8_8scI/edit)
503
504* Artur Janc, Charlie Reis, and Anne van Kesteren's [COOP and COEP Explained](https://docs.google.com/document/d/1Ey3MXcLzwR1T7aarkpBXEwP7jKdd2NvQdgYvF8_8scI/edit)
505
506* Artur Janc's [Notes on the threat model of cross-origin isolation](https://arturjanc.com/coi-threat-model.pdf)