Skip to content

Commit aa2980f

Browse files
annevksefeng211
authored andcommitted
Define opaque-response blocking
This is good enough for early review, but there are a number of issues that still need resolving: https://github.com/annevk/orb/labels/mvp. There are also some inline TODO comments. A PR against HTML is needed to ensure it passes the appropriate metadata for media element and classic script requests. We might also want to depend on HTML for parsing JavaScript.
1 parent 7db7aca commit aa2980f

File tree

1 file changed

+305
-8
lines changed

1 file changed

+305
-8
lines changed

fetch.bs

Lines changed: 305 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ urlPrefix:https://w3c.github.io/hr-time/#;spec:hr-time
5252
urlPrefix:https://tc39.es/ecma262/#;type:dfn;spec:ecma-262
5353
url:realm;text:realm
5454
url:sec-list-and-record-specification-type;text:Record
55-
url:current-realm;text:current realm
55+
url:sec-parsetext;text:ParseText
56+
url:prod-Script;text:Script
57+
url:script-record;text:Script Record
5658
</pre>
5759

5860
<pre class=biblio>
@@ -2161,6 +2163,17 @@ Unless stated otherwise, it is false.
21612163

21622164
<p class=note>This flag is for exclusive use by HTML's render-blocking mechanism. [[!HTML]]
21632165

2166+
<p class=XXX>A <a for=/>request</a> has an associated
2167+
<dfn export for=request>no-cors media request state</dfn> ...
2168+
2169+
<p class=note>This is for exclusive use by the <a>opaque-response-safelist check</a>.
2170+
2171+
<p>A <a for=/>request</a> has an associated
2172+
<dfn for=request>no-cors JavaScript fallback encoding</dfn> (an <a for=/>encoding</a>). Unless
2173+
stated otherwise, it is <a for=/>UTF-8</a>.
2174+
2175+
<p class=note>This is for exclusive use by the <a>opaque-response-safelist check</a>.
2176+
21642177
<hr>
21652178

21662179
<p>A <a for=/>request</a> has an associated
@@ -3275,6 +3288,285 @@ through TLS using ALPN. The protocol cannot be spoofed through HTTP requests in
32753288
</div>
32763289

32773290

3291+
<h3 id=orb>Opaque-response blocking</h3>
3292+
3293+
<div class=note>
3294+
<p>Opaque-response blocking, also known as <abbr>ORB</abbr>, is a network filter that blocks access
3295+
to <a>opaque filtered responses</a>. These responses would likely would not have been useful to the
3296+
fetching party. Blocking them reduces information leakage to potential attackers.
3297+
3298+
<p>Essentially, CSS, JavaScript, images, and media (audio and video) can be requested across
3299+
origins without the <a>CORS protocol</a>. And unfortunately except for CSS there is no MIME type
3300+
enforcement. This algorithm aims to block as many responses as possible that are not one of these
3301+
types (or are newer variants of those types) to avoid leaking their contents through side channels.
3302+
3303+
<p>The network filter combines pro-active blocking based on response headers, sniffing a limited
3304+
set of bytes, and ultimately falls back to a full parse due to unfortunate (lack of) design
3305+
decisions in the early days of the web platform. As a result there are still quite a few responses
3306+
whose secrets can end up being revealed to attackers. Web developers are strongly encouraged to use
3307+
the `<code http-header>Cross-Origin-Resource-Policy</code>` response header to defend them.
3308+
</div>
3309+
3310+
3311+
<h4 id=orb-algorithm>The opaque-response-safelist check</h4>
3312+
3313+
<p>The <dfn>opaque-response-safelist check</dfn>, given a <a for=/>request</a> <var>request</var>
3314+
and a <a for=/>response</a> <var>response</var>, is to run these steps:
3315+
3316+
<ol>
3317+
<li><p>Let <var>mimeType</var> be the result of <a>extracting a MIME type</a> from
3318+
<var>response</var>'s <a for=response>header list</a>.
3319+
3320+
<li><p>Let <var>nosniff</var> be the result of <a>determining nosniff</a> given
3321+
<var>response</var>'s <a for=response>header list</a>.
3322+
3323+
<li>
3324+
<p>If <var>mimeType</var> is not failure, then:
3325+
3326+
<ol>
3327+
<li><p>If <var>mimeType</var> is an <a>opaque-response-safelisted MIME type</a>, then return
3328+
true.
3329+
3330+
<li><p>If <var>mimeType</var> is an <a>opaque-response-blocklisted-never-sniffed MIME type</a>,
3331+
then return false.
3332+
3333+
<li><p>If <var>response</var>'s <a for=response>status</a> is 206 and <var>mimeType</var> is an
3334+
<a>opaque-response-blocklisted MIME type</a>, then return false.
3335+
3336+
<li><p>If <var>nosniff</var> is true and <var>mimeType</var> is an
3337+
<a>opaque-response-blocklisted MIME type</a> or its <a for="MIME type">essence</a> is
3338+
"<code>text/plain</code>", then return false.
3339+
</ol>
3340+
3341+
<li><p>If <var>request</var>'s <a for=request>no-cors media request state</a> is
3342+
"<code>subsequent</code>", then return true.
3343+
3344+
<li><p>If <var>response</var>'s <a for=response>status</a> is 206 and
3345+
<a>validate a partial response</a> given 0 and <var>response</var> returns invalid, then return
3346+
false.
3347+
<!-- TODO Integrate https://wicg.github.io/background-fetch/#validate-a-partial-response into Fetch -->
3348+
3349+
<li><p>Let <var>bytes</var> be the result of running
3350+
<a>obtain a copy of the first 1024 bytes of response</a> given <var>response</var>.
3351+
3352+
<li><p>If <var>bytes</var> is failure, then return false.
3353+
3354+
<li>
3355+
<p>If the <a>audio or video type pattern matching algorithm</a> given <var>bytes</var> does not
3356+
return undefined, then:
3357+
3358+
<ol>
3359+
<li><p>If <var>requests</var>'s <a for=request>no-cors media request state</a> is not
3360+
"<code>initial</code>", then return false.
3361+
3362+
<li><p>If <var>response</var>'s <a for=response>status</a> is not 200 or 206, then return false.
3363+
3364+
<li><p>Return true.
3365+
</ol>
3366+
3367+
<li><p>If <var>requests</var>'s <a for=request>no-cors media request state</a> is not
3368+
"<code>N/A</code>", then return false.
3369+
3370+
<li><p>If the <a>image type pattern matching algorithm</a> given <var>bytes</var> does not return
3371+
undefined, then return true.
3372+
3373+
<li>
3374+
<p>If <var>nosniff</var> is true, then return false.
3375+
3376+
<p class=note>This check is made late as unfortunately images and media are always sniffed.
3377+
3378+
<li><p>If <var>response</var>'s <a for=response>status</a> is not an <a>ok status</a>, then return
3379+
false.
3380+
3381+
<li>
3382+
<p>If <var>mimeType</var> is failure, then return true.
3383+
3384+
<p class=note>This could be improved at somewhat significant cost. See
3385+
<a href=https://github.com/annevk/orb/issues/28>annevk/orb #28</a>.
3386+
3387+
<li><p>If <var>mimeType</var>'s <a for="MIME type">essence</a> <a for=string>starts with</a>
3388+
"<code>audio/</code>", "<code>image/</code>", or "<code>video/</code>", then return false.
3389+
3390+
<li><p>Return <a>determine if response is JavaScript and not JSON</a> given <var>response</var>.
3391+
</ol>
3392+
3393+
<hr>
3394+
3395+
<p>To <dfn>obtain a copy of the first 1024 bytes of response</dfn>, given a <a for=/>response</a>
3396+
<var>response</var>, run these steps:
3397+
3398+
<ol>
3399+
<li><p>Let <var>first1024Bytes</var> be null.
3400+
3401+
<li>
3402+
<p><a for=/>In parallel</a>:
3403+
3404+
<ol>
3405+
<li><p>Let <var>bytes</var> be the empty <a for=/>byte sequence</a>.
3406+
3407+
<li><p>Let <var>transformStream</var> be a new {{TransformStream}}.
3408+
3409+
<li>
3410+
<p>Let <var>transformAlgorithm</var> given a <var>chunk</var> be these steps:
3411+
3412+
<ol>
3413+
<li><p><a for=ReadableStream>Enqueue</a> <var>chunk</var> in <var>transformStream</var>.
3414+
3415+
<li>
3416+
<p>If <var>first1024Bytes</var> is null, then:
3417+
3418+
<ol>
3419+
<li><p>Let <var>chunkBytes</var> be
3420+
<a lt="get a copy of the bytes held by the buffer source">a copy of the bytes held by</a>
3421+
<var>chunk</var>.
3422+
3423+
<li><p>Append <var>chunkBytes</var> to <var>bytes</var>.
3424+
3425+
<li>
3426+
<p>If <var>bytes</var>'s <a for="byte sequencue">length</a> is greater than 1024, then:
3427+
3428+
<ol>
3429+
<li><p>Truncate <var>bytes</var> from the end so that it only contains 1024 bytes.
3430+
3431+
<li><p>Set <var>first1024Bytes</var> to <var>bytes</var>.
3432+
</ol>
3433+
</ol>
3434+
</ol>
3435+
3436+
<li><p>Let <var>flushAlgorithm</var> be this step: if <var>first1024Bytes</var> is null, then set
3437+
<var>first1024Bytes</var> to <var>bytes</var>.
3438+
3439+
<li><p><a for=TransformStream>Set up</a> <var>transformStream</var> with
3440+
<a for="TransformStream/set up"><i>transformAlgorithm</i></a> set to
3441+
<var>transformAlgorithm</var> and <a for="TransformStream/set up"><i>flushAlgorithm</i></a> set
3442+
to <var>flushAlgorithm</var>.
3443+
3444+
<li><p>Set <var>response</var>'s <a for=response>body</a>'s <a for=body>stream</a> to the result
3445+
of <var>response</var>'s <a for=response>body</a>'s <a for=body>stream</a>
3446+
<a for=TransformStream>piped through</a> <var>transformStream</var>.
3447+
</ol>
3448+
3449+
<li><p>Wait until <var>first1024Bytes</var> is non-null or <var>response</var>'s
3450+
<a for=response>body</a>'s <a for=body>stream</a> is <a for=ReadableStream>errored</a>.
3451+
3452+
<li><p>If <var>first1024Bytes</var> is null, then return failure.
3453+
3454+
<li>Return <var>first1024Bytes</var>.
3455+
</ol>
3456+
3457+
<hr>
3458+
3459+
<p>To <dfn>determine if response is JavaScript and not JSON</dfn> given a <a for=/>response</a>
3460+
<var>response</var>, run these steps:</p>
3461+
3462+
<ol>
3463+
<li><p>Let <var>responseBodyBytes</var> be null.
3464+
3465+
<li>
3466+
<p>Let <var>processBody</var> given a <a for=/>byte sequence</a> <var>bytes</var> be these steps:
3467+
3468+
<ol>
3469+
<li><p>Set <var>responseBodyBytes</var> to <var>bytes</var>.
3470+
3471+
<li><p>Set <var>response</var>'s <a for=response>body</a> to the <a for="body with type">body</a>
3472+
of the result of <a for=BodyInit>safely extracting</a> <var>bytes</var>.
3473+
</ol>
3474+
3475+
<li><p>Let <var>processBodyError</var> be this step: set <var>responseBodyBytes</var> to failure.
3476+
3477+
<li><p><a>Fully read</a> <var>response</var>'s <a for=response>body</a> given <a>processBody</a>
3478+
and <var>processBodyError</var>.
3479+
3480+
<li><p>Wait for <var>responseBodyBytes</var> to be non-null.
3481+
3482+
<li><p>If <var>responseBodyBytes</var> is failure, then return false.
3483+
3484+
<li><p><a for=/>Assert</a>: <var>responseBodyBytes</var> is a <a for=/>byte sequence</a>.
3485+
3486+
<li>
3487+
<p>If <a>parse JSON bytes to a JavaScript value</a> given <var>responseBodyBytes</var> does not
3488+
throw, then return false. If it throws, catch the exception and ignore it.
3489+
3490+
<p class=note>If there is an exception, <var>response</var> is not JSON. If there is not, it is.
3491+
3492+
<li><p>Let <var>potentialMIMETypeForEncoding</var> be the result of <a>extracting a MIME type</a>
3493+
given <var>response</var>'s <a for=response>header list</a>.
3494+
3495+
<li>
3496+
<p>Let <var>encoding</var> be the result of <a>legacy extracting an encoding</a> given
3497+
<var>potentialMIMETypeForEncoding</var> and <var>request</var>'s
3498+
<a for=request>no-cors JavaScript fallback encoding</a>.
3499+
3500+
<p class=note>Equivalently to <a>fetch a classic script</a>, this ignores the
3501+
<a for="MIME type" lt=essence>MIME type essence</a>.
3502+
3503+
<li><p>Let <var>sourceText</var> be the result of <a for=/>decoding</a>
3504+
<var>responseBodyBytes</var> given <var>encoding</var>.
3505+
3506+
<li><p>If <a>ParseText</a>(<var>sourceText</var>, <a>Script</a>) returns a <a>Script Record</a>,
3507+
then return true.
3508+
<!-- Ideally HTML owns this so ECMAScript changes don't end up impacting Fetch. We could
3509+
potentially make this use "create a classic script" instead with some mock data. Maybe that is
3510+
better? -->
3511+
3512+
<li><p>Return false.
3513+
</ol>
3514+
3515+
3516+
<h4 id=orb-mime-type-sets>New MIME type sets</h4>
3517+
3518+
<p class=note>The definitions in this section are solely for the purpose of abstracting parts of the
3519+
<a>opaque-response-safelist check</a>. They are not suited for usage elsewhere.
3520+
3521+
<p>An <dfn>opaque-response-safelisted MIME type</dfn> is a <a>JavaScript MIME type</a> or a
3522+
<a for=/>MIME type</a> whose <a for="MIME type">essence</a> is "<code>text/css</code>" or
3523+
"<code>image/svg+xml</code>".
3524+
3525+
<p>An <dfn>opaque-response-blocklisted MIME type</dfn> is an <a>HTML MIME type</a>,
3526+
<a>JSON MIME type</a>, or <a>XML MIME type</a>.
3527+
3528+
<p>An <dfn>opaque-response-blocklisted-never-sniffed MIME type</dfn> is a <a for=/>MIME type</a>
3529+
whose <a for="MIME type">essence</a> is one of:
3530+
3531+
<ul class=brief>
3532+
<li>"<code>application/gzip</code>"
3533+
<li>"<code>application/msexcel</code>"
3534+
<li>"<code>application/mspowerpoint</code>"
3535+
<li>"<code>application/msword</code>"
3536+
<li>"<code>application/msword-template</code>"
3537+
<li>"<code>application/pdf</code>"
3538+
<li>"<code>application/vnd.ces-quickpoint</code>"
3539+
<li>"<code>application/vnd.ces-quicksheet</code>"
3540+
<li>"<code>application/vnd.ces-quickword</code>"
3541+
<li>"<code>application/vnd.ms-excel</code>"
3542+
<li>"<code>application/vnd.ms-excel.sheet.macroenabled.12</code>"
3543+
<li>"<code>application/vnd.ms-powerpoint</code>"
3544+
<li>"<code>application/vnd.ms-powerpoint.presentation.macroenabled.12</code>"
3545+
<li>"<code>application/vnd.ms-word</code>"
3546+
<li>"<code>application/vnd.ms-word.document.12</code>"
3547+
<li>"<code>application/vnd.ms-word.document.macroenabled.12</code>"
3548+
<li>"<code>application/vnd.msword</code>"
3549+
<li>"<code>application/vnd.openxmlformats-officedocument.presentationml.presentation</code>"
3550+
<li>"<code>application/vnd.openxmlformats-officedocument.presentationml.template</code>"
3551+
<li>"<code>application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</code>"
3552+
<li>"<code>application/vnd.openxmlformats-officedocument.spreadsheetml.template</code>"
3553+
<li>"<code>application/vnd.openxmlformats-officedocument.wordprocessingml.document</code>"
3554+
<li>"<code>application/vnd.openxmlformats-officedocument.wordprocessingml.template</code>"
3555+
<li>"<code>application/vnd.presentation-openxml</code>"
3556+
<li>"<code>application/vnd.presentation-openxmlm</code>"
3557+
<li>"<code>application/vnd.spreadsheet-openxml</code>"
3558+
<li>"<code>application/vnd.wordprocessing-openxml</code>"
3559+
<li>"<code>application/x-gzip</code>"
3560+
<li>"<code>application/x-protobuf</code>"
3561+
<li>"<code>application/x-protobuffer</code>"
3562+
<li>"<code>application/zip</code>"
3563+
<li>"<code>multipart/byteranges</code>"
3564+
<li>"<code>multipart/signed</code>"
3565+
<li>"<code>text/event-stream</code>"
3566+
<li>"<code>text/csv</code>"
3567+
</ul>
3568+
3569+
32783570

32793571
<h2 id=http-extensions>HTTP extensions</h2>
32803572

@@ -5237,19 +5529,23 @@ these steps:
52375529
<li><p>Set <var>response</var> and <var>internalResponse</var> to the result of running
52385530
<a>HTTP-network-or-cache fetch</a> given <var>fetchParams</var>.
52395531

5240-
<li>
5241-
<p>If <var>request</var>'s <a for=request>response tainting</a> is "<code>cors</code>" and a
5242-
<a>CORS check</a> for <var>request</var> and <var>response</var> returns failure, then return a
5243-
<a>network error</a>.
5532+
<li><p>If <var>request</var>'s <a for=request>response tainting</a> is "<code>opaque</code>",
5533+
<var>response</var>'s <a for=response>status</a> is not a <a>redirect status</a>, and the
5534+
<a>opaque-response-safelist check</a> given <var>request</var> and <var>response</var> returns
5535+
false, then return a <a>network error</a>.
52445536

5245-
<p class=note>As the <a>CORS check</a> is not to be applied to <a for=/>responses</a> whose
5246-
<a for=response>status</a> is 304 or 407, or <a for=/>responses</a> from a service worker for
5247-
that matter, it is applied here.
5537+
<li><p>If <var>request</var>'s <a for=request>response tainting</a> is "<code>cors</code>" and
5538+
the <a>CORS check</a> for <var>request</var> and <var>response</var> returns failure, then return
5539+
a <a>network error</a>.
52485540

52495541
<li><p>If the <a>TAO check</a> for <var>request</var> and <var>response</var> returns failure,
52505542
then set <var>request</var>'s <a for=request>timing allow failed flag</a>.
52515543
</ol>
52525544

5545+
<p class=note>As the <a>opaque-response-safelist check</a>, <a>CORS check</a>, and
5546+
<a>TAO check</a> are not to be applied to <a for=/>responses</a> whose <a for=response>status</a>
5547+
is 304 or 407, or to <a for=/>responses</a> from a service worker, they are applied here.
5548+
52535549
<li>
52545550
<p>If either <var>request</var>'s <a for=request>response tainting</a> or <var>response</var>'s
52555551
<a for=response>type</a> is "<code>opaque</code>", and the
@@ -9152,6 +9448,7 @@ Mohamed Zergaoui,
91529448
Mohammed Zubair Ahmed<!-- M-ZubairAhmed; GitHub -->,
91539449
Moritz Kneilmann,
91549450
Ms2ger,
9451+
Nathan Froyd,
91559452
Nico Schlömer,
91569453
Nicolás Peña Moreno,
91579454
Nidhi Jaju,

0 commit comments

Comments
 (0)