1# Cross-Origin Read Blocking (CORB)
2
3This document outlines Cross-Origin Read Blocking (CORB), an algorithm by which
4dubious cross-origin resource loads may be identified and blocked by web
5browsers before they reach the web page.
6CORB reduces the risk of leaking sensitive data by keeping it further from
7cross-origin web pages.  In most browsers, it keeps such data out of untrusted
8script execution contexts.  In browsers with
9[Site Isolation](https://www.chromium.org/Home/chromium-security/site-isolation),
10it can keep such data out of untrusted renderer processes entirely, helping even
11against side channel attacks.
12
13[TOC]
14
15## The problem
16
17The same-origin policy generally prevents one origin from reading arbitrary
18network resources from another origin. In practice, enforcing this policy is not
19as simple as blocking all cross-origin loads: exceptions must be established for
20web features, like `<img>` or `<script>` which can target cross-origin
21resources for historical reasons, and for the CORS mechanism which allows some
22resources to be selectively read across origins.
23
24Certain types of content, however, can be shown to be incompatible with all of
25the historically-allowed permissive contexts. JSON is one such type: a JSON
26response will result in a decode error when targeted by the `<img>` tag, either
27a no-op or syntax error when targeted by the `<script>` tag, and so on. The
28only case where a web page can load JSON with observable consequences, is via
29`fetch()` or `XMLHttpRequest`; and in those cases, cross-origin reads are
30moderated by CORS.
31
32By detecting and blocking loads of CORB-protected resources early -- that is,
33before the response makes it to the image decoder or JavaScript parser stage --
34CORB defends against side channel vulnerabilities that may be present in the
35stages which are skipped.
36
37## What attacks does CORB mitigate?
38
39CORB mitigates the following attack vectors:
40
41* Cross-Site Script Inclusion (XSSI)
42  * XSSI is the technique of pointing the `<script>` tag at a target resource
43    which is not JavaScript, and observing some side effects when the resulting
44    resource is interpreted as JavaScript. An early example of this attack was
45    discovered in 2006: by overwriting the JavaScript Array constructor, the
46    contents of JSON lists could be intercepted as simply as:
47      `<script src="https://example.com/secret.json">`.
48    While the array constructor attack vector is fixed in current
49    browsers, numerous similar exploits have been found and fixed in the
50    subsequent decade. For example, see the
51    [slides here](https://www.owasp.org/images/6/6a/OWASPLondon20161124_JSON_Hijacking_Gareth_Heyes.pdf).
52  * CORB prevents this class of attacks, because a CORB-protected resource will
53    be blocked from ever being delivered to a cross-site `<script>` element.
54  * CORB is particularly valuable in absence of other XSSI defenses like
55    [XSRF tokens](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#synchronizer-token-pattern)
56    and/or
57    [JSON security prefixes](https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection).
58    Additionally, the presence of XSSI defenses like
59    [JSON security prefixes](https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection)
60    can also be used as a signal to the CORB algorithm that a resource should be
61    CORB-protected.
62
63* Speculative Side Channel Attack (e.g. Spectre).
64  * For example, an attacker may use an
65    `<img src="https://example.com/secret.json">`
66    element to pull a cross-site secret
67    into the process where the attacker's JavaScript runs, and then use a
68    speculative side channel attack (e.g. [Spectre](https://spectreattack.com))
69    to read the secret.
70  * CORB can prevent this class of attacks when used in tandem with
71    [Site Isolation](https://www.chromium.org/Home/chromium-security/site-isolation),
72    by preventing the JSON resource from being present in the
73    memory of a process hosting a cross-site page.
74
75## How does CORB "block" a response?
76
77When CORB decides that a response needs to be CORB-protected, the response is
78modified as follows:
79* The response body is replaced with an empty body.
80* The response headers are removed.
81
82> [lukasza@chromium.org] Chromium currently retains Access-Control-\* headers
83> (this helps generate better error messages for CORS).
84
85To be effective against speculative side-channel attacks, CORB blocking must
86take place before the response reaches the process hosting the cross-origin
87initiator of the request.  In other words, CORB blocking should prevent
88CORB-protected response data from ever being present in the memory of the
89process hosting a cross-origin website (even temporarily or for a short term).
90This is different from the concept of
91[filtered responses](https://fetch.spec.whatwg.org/#concept-filtered-response)
92(e.g. [CORS filtered response](https://fetch.spec.whatwg.org/#concept-filtered-response-cors) or
93[opaque filtered response](https://fetch.spec.whatwg.org/#concept-filtered-response-opaque))
94which just provide a limited view into full data that remains stored in an
95[internal response](https://fetch.spec.whatwg.org/#concept-internal-response)
96and may be implemented inside the renderer process.
97
98A CORB demo page
99[is available here](https://anforowicz.github.io/xsdb-demo/index.html).
100
101## What kinds of requests are CORB-eligible?
102
103The following kinds of requests are CORB-exempt:
104
105* [navigation requests](https://fetch.spec.whatwg.org/#navigation-request)
106  or requests where the
107  [request destination](https://fetch.spec.whatwg.org/#concept-request-destination)
108  is "object" or "embed".
109  Cross-origin `<iframe>`s, `<object>`s, and `<embed>`s create a separate
110  security context and thus pose less risk for leaking the data.  In most
111  browsers, this separate context means that a malicious page would have more
112  trouble inferring the contents than from loading them into its own execution
113  context and observing side effects (e.g., XSSI, style tags, etc).  In browsers
114  with Site Isolation, this security context uses a separate process, keeping
115  the data out of the malicious page's address space entirely.
116
117> [lukasza@chromium.org] TODO: Figure out how
118> [Edge's VM-based isolation](https://cloudblogs.microsoft.com/microsoftsecure/2017/10/23/making-microsoft-edge-the-most-secure-browser-with-windows-defender-application-guard/)
119> works (e.g. if some origins are off-limits in particular renderers, then this
120> would greatly increase utility of CORB in Edge).
121
122* Download requests (e.g. requests where the
123  [initiator](https://fetch.spec.whatwg.org/#concept-request-initiator)
124  is "download").  In this case the data from the response is saved to disk
125  (instead of being shared to a cross-origin context) and therefore wouldn't
126  benefit from CORB protection.
127
128> [lukasza@chromium.org] AFAIK, in Chrome a response to a download request never
129> passes through memory of a renderer process.  Not sure if this is true in
130> other browsers.
131
132All other kinds of requests may be CORB-eligible.  This includes:
133* [XHR](https://xhr.spec.whatwg.org/)
134  and [fetch()](https://fetch.spec.whatwg.org/#dom-global-fetch)
135* `ping`, `navigator.sendBeacon()`
136* `<link rel="prefetch" ...>`
137* Requests with the following
138  [request destination](https://fetch.spec.whatwg.org/#concept-request-destination):
139    - "image" requests like `<img>` tag, `/favicon.ico`, SVG's `<image>`,
140      CSS' `background-image`, etc.
141    - [script-like destinations](https://fetch.spec.whatwg.org/#request-destination-script-like)
142      like `<script>`, `importScripts()`, `navigator.serviceWorker.register()`,
143      `audioWorklet.addModule()`, etc.
144    - "audio", "video" or "track"
145    - "font"
146    - "style"
147    - "report" requests like CSP reports, NEL reports, etc.
148
149The essential idea of CORB is to consider whether a particular resource might be
150unsuitable for use in *every* context listed above. If every possible usage
151would result in either a CORS error, a syntax/decoding error, or no observable
152consequence, CORB ought to be able to block the cross-origin load without
153changing the observable consequences of the load. Prior to CORB, details are
154already suppressed from cross-origin errors, to prevent information leaks. Thus,
155the observable consequences of such errors are already limited, and feasible to
156preserve while blocking.
157
158## What types of content are protected by CORB?
159
160As discussed below, the following types of content are CORB-protected:
161 * JSON
162 * HTML
163 * XML
164
165These are each discussed in the following sections.
166
167### Protecting JSON
168
169JSON is a widely used data format on the web; support for JSON is built into the
170web platform. JSON responses are very likely to contain user data worth
171protecting. Additionally, unlike HTML or image formats, there are no legacy HTML
172mechanisms (that is, predating CORS) which allow cross-origin embedding of JSON
173resources.
174
175Because the JSON syntax is derived from and overlaps with JavaScript, care must
176be taken to handle the possibility of JavaScript/JSON polyglots.
177CORB handles the following cases for JSON:
178 * Non-empty JSON object literal: A non-empty JSON object
179   (such as `{"key": "value"}`). This is precisely the subset of JSON syntax
180   which is invalid JavaScript syntax -- the colon after the first string
181   literal will generate a syntax error. CORB can protect these cases, even if
182   labeled with a different Content-Type, by sniffing the response body.
183 * Other JSON literals: The remaining subset of the JSON syntax (for example,
184   `null` or `[1, 2, "3"]`) also happens to be valid JavaScript syntax. In
185   particular, when evaluated as script, they are value expressions that should
186   have no side effects. Thus, if they can be detected, they can be CORB-
187   protected. Detection here is possible, but requires implementing a validator
188   that understands the full JSON syntax:
189    * If the response is not labeled with a JSON Content Type, CORB might detect
190      these cases by buffering and validating the entire response body as
191      JSON; the entire response must be considered because of the potential for
192      a valid, side-effect-having JavaScript program like `[1, 2,
193      "3"].map(...)`.
194    * If the response is indeed labeled with a JSON Content Type, CORB may
195      decide to sniff the response to confirm it is valid JSON, only up to a
196      certain number of bytes. This would avoid buffering and parsing
197      in an unbounded amount of memory.
198 * JSON served with
199   [an XSSI-defeating prefix](https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection):
200   As a mitigation for past browser
201   vulnerabilities, many actual websites and frameworks employ a convention of
202   prefixing their fetchable resources with a string designed to force a
203   JavaScript error.
204   These prefixes have not been standardized prior to CORB, but a few approaches
205   seem prevalent:
206
207    * The character sequence `)]}'` is built into
208      [the angular.js framework](https://docs.angularjs.org/api/ng/service/$http),
209      [the Java Spring framework](https://goo.gl/xP7FWn),
210      and is observed in wide use on the google.com domain.
211    * The character sequence `{} &&` was
212      [historically built into the Java Spring framework](https://goo.gl/JYPFAv).
213    * Infinite loops, such as `for(;;);`, are observed in wide use on the
214      facebook.com domain.
215
216   The presence of these recognized XSSI defenses is a
217   strong signal to the CORB algorithm that a resource should be CORB-protected.
218   As such, these prefixes should trigger CORB protection in almost every case,
219   no matter what follows them. This is argued to be safe because:
220     * [A JSON security prefix](https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection)
221       would cause a syntax error (or a hang) if present in a document served
222       with a JavaScript MIME type such as `text/javascript`.
223     * [JSON security prefixes](https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection)
224       are not known to collide with binary
225       resources like images, videos or fonts (which typically require
226       the first few bytes to be hardcoded to a specific sequence - for example
227       `FF D8 FF` for image/jpeg).
228     * Collisions with `text/css` stylesheets are theoretically possible, because
229       it is possible to construct a file that begins with
230       [a JSON security prefix](https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection),
231       but at the same parses fine as a stylesheet.
232       `text/css` is therefore established as an exception, even though the
233       practical likelihood of such a scenario seems low.
234       See below for an example of such a stylesheet:
235```css
236)]}'
237{}
238h1 { color: red; }
239```
240
241JSON is also used by some web features. One example is `<link
242rel="manifest">`, whose `href` attribute specifies a JSON manifest file.
243Fortunately, this mechanism requires CORS when the manifest is specified cross-
244origin, so its CORB treatment works identically to the rules applied to fetch().
245
246> [nick@chromium.org] TODO: Is there a spec link for JSON being side-effect
247> free when interpreted as script?
248
249### Protecting HTML
250
251HTML can be embedded cross-origin via `<iframe>` (as noted above),
252but otherwise HTML documents can
253only be loaded by fetch() and XHR, both of which require CORS. HTML sniffing is
254already well-understood, so (unlike JSON) it is relatively easy to identify HTML
255resources with high confidence.
256
257One ambiguous polyglot case has been
258identified that CORB needs to handle conservatively: HTML-style comments, which
259are [part of the JavaScript syntax](https://www.ecma-international.org/ecma-262/8.0/index.html#sec-html-like-comments).
260* CORB skips over HTML comment blocks when sniffing to
261  confirm a HTML content type.  This means that (unlike in
262  [normal HTML sniffing](https://mimesniff.spec.whatwg.org/#identifying-a-resource-with-an-unknown-mime-type))
263  presence of "`<!--`" string doesn't immediately confirm that the sniffed resource is a
264  HTML document - the HTML comment still has to be followed by a valid HTML tag.
265* Additionally, after the end of a HTML comment, the CORB sniffer will skip all
266  characters until a line terminating character.  This helps accomodate the
267  [`SingleLineHTMLCloseComment`](https://www.ecma-international.org/ecma-262/8.0/index.html#prod-annexB-SingleLineHTMLCloseComment)
268  rule which can consume
269  [`SingleLineCommentChars`](https://www.ecma-international.org/ecma-262/8.0/index.html#prod-SingleLineCommentChars)
270  _after_ the "`-->`" characters.
271
272Examples of html/javascript polyglots which have been observed
273in use on real websites:
274```js
275<!--/*--><html><body><script type="text/javascript"><!--//*/
276var x = "This is both valid html and valid javascript";
277//--></script></body></html>
278```
279
280```js
281<!-- comment --> <script type='text/javascript'>
282//<![CDATA[
283var x = "This is both valid html and valid javascript";
284//]]>--></script>
285```
286
287
288### Protecting XML
289
290XML, like JSON, is a widely used data exchange format, and like HTML, is a
291document format that's built into the web platform (notably via XmlHttpRequest).
292
293Confirming an XML content-type via sniffing is more straightforward than JSON or
294HTML: XML is signified by the pattern `<?xml`, possibly preceded by whitespace.
295
296The only identified XML case that requires special treatment by CORB is
297`image/svg+xml`, which is an image type. All other XML mime types are treated as
298CORB-protected.
299
300## Determining whether a response is CORB-protected
301
302CORB decides whether a response needs protection (i.e. if a response is a JSON,
303HTML or XML resource) based on the following:
304
305* If the response contains `X-Content-Type-Options: nosniff` response header,
306  then the response will be CORB-protected
307  if its `Content-Type` header is one of the following:
308  * [HTML MIME type](https://mimesniff.spec.whatwg.org/#html-mime-type)
309  * [XML MIME type](https://mimesniff.spec.whatwg.org/#xml-mime-type)
310    (except `image/svg+xml` which is CORB-exempt as described above)
311  * [JSON MIME type](https://mimesniff.spec.whatwg.org/#json-mime-type)
312  * `text/plain`
313
314* If the response is a 206 response,
315  then the response will be CORB-protected
316  if its `Content-Type` header is one of the following:
317  * [HTML MIME type](https://mimesniff.spec.whatwg.org/#html-mime-type)
318  * [XML MIME type](https://mimesniff.spec.whatwg.org/#xml-mime-type)
319    (except `image/svg+xml` which is CORB-exempt as described above)
320  * [JSON MIME type](https://mimesniff.spec.whatwg.org/#json-mime-type)
321
322* Otherwise, CORB attempts to sniff the response body:
323  * [HTML MIME type](https://mimesniff.spec.whatwg.org/#html-mime-type)
324    that sniffs as HTML is CORB-protected
325  * [XML MIME type](https://mimesniff.spec.whatwg.org/#xml-mime-type)
326    (except `image/svg+xml`) that sniffs as XML is CORB-protected
327  * [JSON MIME type](https://mimesniff.spec.whatwg.org/#json-mime-type)
328    that sniffs as JSON is CORB-protected
329  * `text/plain` that sniffs as JSON, HTML or XML is CORB-protected
330  * Any response (except `text/css`) that begins with
331    [a JSON security prefix](https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection)
332    is CORB-protected
333
334The sniffing is necessary to avoid blocking existing web pages that depend on
335mislabeled cross-origin responses (e.g. on images served as `text/html`).
336Without sniffing CORB would block around 16 times as many
337responses.
338* CORB will only sniff to *confirm* the classification based on the `Content-Type`
339  header (i.e. if the `Content-Type` header is `text/json` then CORB will sniff
340  for JSON and will not sniff for HTML or XML).
341* If some syntax elements are shared between CORB-protected and
342  non-CORB-protected MIME types, then these elements have to be ignored by CORB
343  sniffing.  For example, when sniffing for (CORB-protected) HTML, CORB ignores
344  and skips HTML comments, because
345  [they can also be present](http://www.ecma-international.org/ecma-262/6.0/#sec-html-like-comments)
346  in (non-CORB-protected) JavaScript.  This is different from the
347  [HTML sniffing rules](https://mimesniff.spec.whatwg.org/#rules-for-identifying-an-unknown-mime-type),
348  used in other contexts.
349* Sniffing is a best-effort heuristic and for best security results, we
350  recommend that web developers 1) mark responses with the correct `Content-Type`
351  header and 2) opt out of sniffing by using the
352  `X-Content-Type-Options: nosniff` header.
353
354> [nick@chromium.org] This section needs a strong justification for why
355> text/plain gets this special interpretation. Ideally data showing that
356> text/plain is commonly used to serve HTML, JSON, or XML. Treatment of
357> text/plain in our current implementation may actually be an artifact of
358> an earlier prototype, which ran after standard mime sniffing, and may have
359> seen 'text/plain' MIME types applied as a default MIME type when the response
360> omitted a Content-Type header.
361
362Note that the above means that the following responses are not CORB-protected:
363* Responses labeled as `multipart/*`.
364  This avoids having to parse the content types of the nested parts.
365  We recommend not supporting multipart range requests for sensitive documents.
366* Responses without a `Content-Type` header.
367* Responses with a JavaScript MIME type such as `text/javascript`. This
368  includes JSONP ("JSON with padding") which unlike JSON is meant to be read
369  and executed in a cross-origin context.
370
371
372## CORB and web compatibility
373
374### Observable CORB impact on images
375
376CORB should have no observable impact on `<img>` tags unless the image resource
377is both 1) mislabeled with an incorrect, non-image, CORB-protected Content-Type
378and 2) served with the `X-Content-Type-Options: nosniff` response header.
379
380Examples:
381
382* **Correctly-labeled HTML document**
383  * Resource used in an `<img>` tag:
384    * Body: an HTML document
385    * `Content-Type: text/html`
386    * No `X-Content-Type-Options` header
387  * Expected behavior: **no observable difference**.
388    The rendered image should be the same broken image when 1) attempting
389    to render an html document as an image (without CORB) and 2) attempting to
390    render an empty response as an image (when CORB blocks the response).
391  * WPT test: `fetch/corb/img-html-correctly-labeled.sub.html`
392
393* **Mislabeled image (with sniffing)**
394  * Resource used in an `<img>` tag:
395    * Body: an image
396    * `Content-Type: text/html`
397    * No `X-Content-Type-Options` header
398  * Expected behavior: **no difference**.
399    CORB will sniff that the response body is *not* actually a CORB-protected
400    type and therefore will allow the response.
401  * WPT test: `fetch/corb/img-png-mislabeled-as-html.sub.html`
402
403* **Mislabeled image (nosniff)**
404  * Resource used in an `<img>` tag:
405    * Body: an image
406    * `Content-Type: text/html`
407    * `X-Content-Type-Options: nosniff`
408  * Expected behavior: **observable difference**.
409    Because of the `nosniff` header, CORB will have to rely on the
410    `Content-Type` header.  Because this response is mislabeled (the body is an
411    image, but the `Content-Type` header says that it is a html document), CORB
412    will incorrectly classify the response as requiring CORB-protection.
413  * WPT test: `fetch/corb/img-png-mislabeled-as-html-nosniff.tentative.sub.html`
414
415In addition to the HTML `<img>` tag, the examples above should apply to other
416web features that consume images - including, but not limited to:
417* `/favicon.ico`
418* SVG's `<image>`,
419* `<link rel="preload" as="image" ...>` (see WPT test:
420  `fetch/corb/preload-image-png-mislabeled-as-html-nosniff.tentative.sub.html`)
421* `background-image` in stylesheets
422* painting images onto (potentially tainted) HTML's `<canvas>`
423
424> [lukasza@chromium.org] Earlier attempts to block nosniff images with
425> incompatible MIME types
426> [failed](https://github.com/whatwg/fetch/issues/395).
427> We think that CORB will have more luck, because
428> it will only block a subset of CORB-protected MIME types
429> (e.g. it won't block `application/octet-stream` as quoted in a
430> [Firefox bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1302539))
431
432
433### Observable CORB impact on multimedia
434
435Audio and video resources should see similar impact as images, though 206
436responses are more likely to occur for media.
437
438### Observable CORB impact on scripts
439
440CORB should have no observable impact on `<script>` tags except for cases where
441a CORB-protected, non-JavaScript resource labeled with its correct MIME type is
442loaded as a script - in these cases the resource will usually result in a syntax
443error, but CORB-protected response's empty body will result in no error.
444
445Examples:
446
447* **Correctly-labeled HTML document**
448  * Resource used in a `<script>` tag:
449    * Body: an HTML document
450    * `Content-Type: text/html`
451    * No `X-Content-Type-Options` header
452  * Expected behavior: **observable difference** via
453    [GlobalEventHandlers.onerror](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror).
454    Most CORB-protected resources would result in a syntax error when parsed as
455    JavaScript.  The syntax error goes away after CORB blocks a response,
456    because the empty body of the blocked response parses fine as JavaScript.
457  * WPT test: `fetch/corb/script-html-correctly-labeled.tentative.sub.html`
458
459> [lukasza@chromium.org] In theory, using a non-empty response in CORB-blocked
460> responses might reintroduce the lost syntax error.  We didn't go down that
461> path, because
462> 1) using a non-empty response would be inconsistent with other parts of the
463>    Fetch spec (like
464>    [opaque filtered response](https://fetch.spec.whatwg.org/#concept-filtered-response-opaque)).
465> 2) retaining the presence of the syntax error might require changing the
466>    contents of a CORB-blocked response body depending on whether the original
467>    response body would have caused a syntax error or not.  This would add
468>    extra complexity that seems undesirable both for CORB implementors and for
469>    web developers.
470
471* **Mislabeled script (with sniffing)**
472  * Resource used in a `<script>` tag:
473    * Body: a proper JavaScript script
474    * `Content-Type: text/html`
475    * No `X-Content-Type-Options` header
476  * Expected behavior: **no difference**.
477    CORB will sniff that the response body is *not* actually a CORB-protected
478    type and therefore will allow the response.  Note that CORB sniffing is
479    resilient to the fact that some syntax elements are shared across HTML
480    and JavaScript (e.g.
481    [comments](http://www.ecma-international.org/ecma-262/6.0/#sec-html-like-comments)).
482  * WPT test: `fetch/corb/script-js-mislabeled-as-html.sub.html`
483
484* **Mislabeled script (nosniff)**
485  * Resource used in a `<script>` tag:
486    * Body: a proper JavaScript script
487    * `Content-Type: text/html`
488    * `X-Content-Type-Options: nosniff`
489  * Expected behavior: **no observable difference**.
490    Both with and without CORB, the script will not execute, because the
491    `nosniff` response header response will cause the response to be blocked
492    when its MIME type (`text/html` in the example) is not a
493    [JavaScript MIME type](https://html.spec.whatwg.org/multipage/scripting.html#javaScript-mime-type)
494    (this behavior is required by the
495    [Fetch spec](https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?)).
496  * WPT test: `fetch/corb/script-js-mislabeled-as-html-nosniff.sub.html`
497
498In addition to the HTML `<script>` tag, the examples above should apply to other
499web features that consume JavaScript including
500[script-like destinations](https://fetch.spec.whatwg.org/#request-destination-script-like)
501like `importScripts()`, `navigator.serviceWorker.register()`,
502`audioWorklet.addModule()`, etc.
503
504
505### Observable CORB impact on stylesheets
506
507CORB should have no observable impact on stylesheets.
508
509Examples:
510
511* **Anything not labeled as text/css**
512  * Examples of resources used in a `<link rel="stylesheet" href="...">` tag:
513    * Body: an HTML document OR a simple, valid stylesheet OR a polyglot
514      HTML/CSS stylesheet
515    * `Content-Type: text/html`
516    * No `X-Content-Type-Options` header
517  * Expected behavior: **no observable difference**.
518    Even without CORB, such stylesheet examples will be rejected, because
519    due to the
520    [relaxed syntax rules](https://scarybeastsecurity.blogspot.dk/2009/12/generic-cross-browser-cross-domain.html)
521    of CSS, cross-origin CSS requires a correct Content-Type header
522    (restrictions vary by browser:
523    [IE](http://msdn.microsoft.com/en-us/library/ie/gg622939%28v=vs.85%29.aspx),
524    [Firefox](http://www.mozilla.org/security/announce/2010/mfsa2010-46.html),
525    [Chrome](https://bugs.chromium.org/p/chromium/issues/detail?id=9877),
526    [Safari](http://support.apple.com/kb/HT4070)
527    (scroll down to CVE-2010-0051) and
528    [Opera](http://www.opera.com/support/kb/view/943/)).
529    This behavior is covered by
530    [the HTML spec](https://html.spec.whatwg.org/#link-type-stylesheet)
531    which 1) asks to only assume `text/css` Content-Type
532    if the document embedding the stylesheet has been set to quirks mode and has
533    the same origin and 2) only asks to run the steps for creating a CSS style
534    sheet if Content-Type of the obtained resource is `text/css`.
535
536  * WPT tests:
537    `fetch/corb/style-css-mislabeled-as-html.sub.html`,
538    `fetch/corb/style-html-correctly-labeled.sub.html`
539
540* **Anything not labeled as text/css (nosniff)**
541  * Resource used in a `<link rel="stylesheet" href="...">` tag:
542    * Body: a simple stylesheet
543    * `Content-Type: text/html`
544    * `X-Content-Type-Options: nosniff`
545  * Expected behavior: **no observable difference**.
546    Both with and without CORB, the stylesheet will not load, because the
547    `nosniff` response header response will cause the response to be blocked
548    when its MIME type (`text/html` in the example) is not `text/css`
549    (this behavior is required by the
550    [Fetch spec](https://fetch.spec.whatwg.org/#should-response-to-request-be-blocked-due-to-nosniff?)).
551  * WPT test: `fetch/corb/style-css-mislabeled-as-html-nosniff.sub.html`
552
553* **Correctly-labeled stylesheet with a JSON security prefix**
554  * Resource used in a `<link rel="stylesheet" href="...">` tag:
555    * Body: a stylesheet that begins with
556      [a JSON security prefix](https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection)
557    * `Content-Type: text/css`
558    * No `X-Content-Type-Options` header
559  * Expected behavior: **no difference**,
560    because CORB sniffing for
561    [JSON security prefixes](https://docs.angularjs.org/api/ng/service/$http#json-vulnerability-protection)
562    is not performed for responses labeled as `Content-Type: text/css`.
563  * WPT test: `fetch/corb/style-css-with-json-parser-breaker.sub.html`
564
565
566### Observable CORB impact on other web platform features
567
568CORB has no impact on the following scenarios:
569
570* **[XHR](https://xhr.spec.whatwg.org/) and
571  [fetch()](https://fetch.spec.whatwg.org/#dom-global-fetch)**
572  * CORB has no observable effect, because [XHR](https://xhr.spec.whatwg.org/)
573    and [fetch()](https://fetch.spec.whatwg.org/#dom-global-fetch) already apply
574    same-origin policy to the responses (e.g. CORB should only block responses
575    that would result in cross-origin XHR errors because of lack of CORS).
576
577* **[Prefetch](https://html.spec.whatwg.org/#link-type-prefetch)**
578  * CORB blocks response body from reaching a cross-origin renderer, but
579    CORB doesn't prevent the response body from being cached by the browser
580    process (and subsequently delivered into another, same-origin renderer
581    process).
582
583* **Tracking and reporting**
584  * Various techniques try to check that a user has accessed some content
585    by triggering a web request to a HTTP server that logs the user visit.
586    The web request is frequently triggered by an invisible `img` element
587    to a HTTP URI that usually replies either with a 204 or with a
588    short HTML document.  In addition to the `img` tag, websites may use
589    `style`, `script` and other tags to track usage.
590  * CORB won't impact these techniques, as they don't depend on the actual
591    content being returned by the HTTP server.  This also applies to other
592    web features that don't care about the response: beacons, pings, CSP
593    violation reports, etc.
594
595* **Service workers**
596  * Service workers may intercept cross-origin requests and artificially
597    construct a response *within* the service worker (i.e. without crossing
598    origins and/or security boundaries).  CORB will not block such responses.
599  * When service workers cache actual cross-origin responses (e.g. in 'no-cors'
600    request mode), the responses are 'opaque' and therefore CORB can block such
601    responses without changing the service worker's behavior ('opaque' responses
602    have a non-accessible body even without CORB).
603
604* **Blob and File API**
605  * Fetching cross-origin blob URLs is blocked even without CORB
606    (see https://github.com/whatwg/fetch/issues/666).
607  * WPT test: `script-html-via-cross-origin-blob-url.sub.html`
608    (and also tests for navigation requests covered by the
609    [commit here](https://github.com/mkruisselbrink/web-platform-tests/commit/9524a71919340eacc8aaa6e55ffe0b5aa72f9bfd)).
610
611* **Content scripts and plugins**
612  * These are not covered by CORB - CORB assumes that that appropriate security
613    policies are enforced by some other mechanism for content scripts and
614    plugins (e.g. Adobe Flash implements a CORS-like mechanism via
615    [crossdomain.xml](https://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html)).
616
617### Quantifying CORB impact on existing websites
618
619CORB has been enabled in optional Site Isolation modes and field trials, and
620Chromium has been instrumented to count how many CORB-eligible responses are
621blocked.  (CORB-eligible responses exclude
622[navigation requests](https://fetch.spec.whatwg.org/#navigation-request) and
623downloads; see the "What kinds of requests are CORB-eligible?" section above.)
624Our analysis of the initial data from Chrome Canary in February 2018 shows a low
625upper bound on the number of cases observable to web pages, with possibilities
626to further lower the bounds.
627
628Overall, **0.961% of all CORB-eligible responses are blocked.**  However, over
629half of these are empty responses already (i.e., actually have a
630`Content-Length: 0` response header), and thus cause effectively no behavior
631change (i.e., only
632[non-safelisted](https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name)
633headers would be affected).  Note that if sniffing were omitted, almost 20% of
634responses would be blocked, so sniffing is a clear necessity.
635
636Looking closer, **0.456% of all CORB-eligible responses are non-empty and
637blocked.**  However, most of these cases fall into the non-observable categories
638described in the subsections above, such as HTML responses being delivered to
639image tags as tracking pixels.
640
641We can focus on two groups of blocked responses which may have observable
642impact.
643
644* **0.115% of all CORB-eligible responses might have been observably blocked due
645  to a nosniff header or range request.**  This is specific to non-empty
646  responses with a status code other than 204, requested from a context that
647  doesn't otherwise ignore mislabeled nosniff content (e.g., as script tags
648  would).  Within this group:
649  * 95.16% of these are nosniff responses labeled as HTML requested by an image
650    tag.  These are blocked and could possibly contain real images.  However, we
651    expect many of these cases actually contained HTML and would not have
652    rendered in the image tag anyway (as we observed in one case).
653
654> [creis@chromium.org] We are considering lowering this bound further by
655> sniffing these responses to confirm how many might contain actual images.
656
657  * Another 3.76% of these are range requests for text/plain from a media
658    context.  We have not yet found examples in practice, but we are considering
659    allowing range request responses for text/plain to avoid disruption here.
660
661* **0.014% of all CORB-eligible responses were invalid inputs to script tags**,
662  since CORB sniffing revealed they were HTML, XML, or JSON.  Again, this is
663  specific to non-empty responses that do not have a 204 status code.  These
664  cases should have minimal risk of disruption in practice (e.g., more than half
665  have error status codes and likely represent broken links), but it is
666  technically possible to observe a difference based on whether a syntax error
667  is reported.
668
669These numbers of affected cases are sufficiently low to suggest that CORB is
670promising from a web compatibility perspective.
671
672
673## Appendix: Future work - protecting more resource types
674
675The currently proposed version of CORB only protects JSON, HTML and XML
676resources - other sensitive resources need to be protected in some other way.
677One possible approach is to protect such resources via unguessable XSRF tokens
678which are distributed via JSON (which is CORB-protected).
679
680In the future CORB may be extended to protect additional resources as follows:
681
682* **Covering more MIME types**.
683  Instead of blacklisting HTML, XML, and JSON, CORB protection can be extended to
684  all MIME types, except MIME types that are whitelisted as usable in `<img>`,
685  `<audio>`, `<video>`, `<script>` and other similar elements that can be
686  embedded cross-origin:
687    * [JavaScript MIME type](https://html.spec.whatwg.org/#javascript-mime-type)
688      like `text/javascript`, `application/javascript`, or `text/jscript`
689    * `text/css`
690    * [image types](https://mimesniff.spec.whatwg.org/#image-type) like types
691      matching `image/*`
692    * [audio or video types](https://mimesniff.spec.whatwg.org/#audio-or-video-type)
693      like `audio/*`, `video/*` or `application/ogg`
694    * `font/*` or one of legacy
695      [font types](https://mimesniff.spec.whatwg.org/#font-type)
696    * Other MIME types like
697      `application/octet-stream`,
698      [text/vtt](https://w3c.github.io/webvtt/#file-structure)
699
700  This extension would offer CORB-protection to resources like PDFs or ZIP files.
701  CORB would not perform confirmation sniffing for MIME types other than HTML,
702  XML and JSON (since it is not practical to teach CORB sniffer about *all* the
703  possible MIME types).  On the other hand, the value of confirmation sniffing
704  for these other MIME types seems low, since mislabeling content as such
705  types seems less likely than for example mislabeling as `text/html`.
706
707> [lukasza@chromium.org] See also https://github.com/whatwg/fetch/issues/721
708
709* **CORB opt-in header**.
710  To protect resources that normally may be embedded cross-origin,
711  a server might explicitly opt into CORB with a HTTP response header.
712  This would make it possible to CORB-protect resources like
713  images or JavaScript (including JSONP).
714
715> [lukasza@chromium.org] Currently considered CORB opt-in signals include:
716> - `From-Origin:` or `Cross-Origin-Resource-Policy:` header - see https://github.com/whatwg/fetch/issues/687
717> - `Isolate-Me` header - see https://github.com/WICG/isolation
718
719## Appendix: CORB and web standards
720
721[The CORB section in the Fetch spec](https://fetch.spec.whatwg.org/#corb) covers
722handling of `nosniff` and 206 responses since
723[May 2018](https://github.com/whatwg/fetch/pull/686).
724
725CORB confirmation sniffing is not standardized yet.
726
727[Some aspects of CORB](https://github.com/whatwg/fetch/issues?utf8=%E2%9C%93&q=is%3Aissue+CORB+)
728are under discussion and may evolve over time.
729
730## Appendix: CORB implementation status
731
732Tracking bugs:
733- Chrome: https://crbug.com/268640 and https://crbug.com/802835 and https://www.chromestatus.com/feature/5629709824032768
734- Edge: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/17382911/
735- Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1459357
736- Safari/WebKit: https://bugs.webkit.org/show_bug.cgi?id=185331
737
738Status of Web Platform Tests:
739- [Experimental builds](https://master-dot-wptdashboard.appspot.com/results/fetch/corb?label=experimental)
740- [Stable releases](https://master-dot-wptdashboard.appspot.com/results/fetch/corb)
741