Skip to content

Commit

Permalink
Define opaque-response blocking
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
annevk committed Jun 1, 2022
1 parent 82bb961 commit d4ab1db
Showing 1 changed file with 216 additions and 1 deletion.
217 changes: 216 additions & 1 deletion fetch.bs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ urlPrefix:https://w3c.github.io/hr-time/#;spec:hr-time
url:dfn-coarsened-shared-current-time;text:coarsened shared current time
url:dfn-unsafe-shared-current-time;text:unsafe shared current time
type:typedef;url:dom-domhighrestimestamp;text:DOMHighResTimeStamp

urlPrefix: https://tc39.es/ecma262/;type:dfn;spec:ecma-262
url:sec-parsetext;text:ParseText
url:prod-Script;text:Script
url:script-record;text:Script Record
</pre>

<pre class=link-defaults>
Expand Down Expand Up @@ -1844,6 +1849,17 @@ Unless stated otherwise, it is false.

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

<p class=XXX>A <a for=/>request</a> has an associated
<dfn export for=request>no-cors media request state</dfn> ...

<p class=note>This is for exclusive use by the <a>opaque-response-safelist check</a>.

<p>A <a for=/>request</a> has an associated
<dfn for=request>no-cors JavaScript fallback encoding</dfn> (an <a for=/>encoding</a>). Unless
stated otherwise, it is <a for=/>UTF-8</a>.

<p class=note>This is for exclusive use by the <a>opaque-response-safelist check</a>.

<hr>

<p>A <a for=/>request</a> has an associated
Expand Down Expand Up @@ -2876,6 +2892,198 @@ run these steps:
</ol>


<h3 id=orb>Opaque-response blocking</h3>

<div class=note>
<p>Opaque-response blocking, also known as <abbr>ORB</abbr>, is a network filter that blocks access
to <a>opaque filtered responses</a>. These responses would likely would not have been useful to the
fetching party. Blocking them reduces information leakage to potential attackers.

<p>Essentially, CSS, JavaScript, images, and media (audio and video) can be requested across
origins without the <a>CORS protocol</a>. And unfortunately except for CSS there is no MIME type
enforcement. This algorithm aims to block as many responses as possible that are not one of these
types (or are newer variants of those types) to avoid leaking their contents through side channels.

<p>The network filter combines pro-active blocking based on response headers, sniffing a limited
set of bytes, and ultimately falls back to a full parse due to unfortunate (lack of) design
decisions in the early days of the web platform. As a result there are still quite a few responses
whose secrets can end up being revealed to attackers. Web developers are strongly encouraged to use
the `<code http-header>Cross-Origin-Resource-Policy</code>` response header to defend them.
</div>


<h4 id=orb-algorithm>The opaque-response-safelist check</h4>

<p>The <dfn>opaque-response-safelist check</dfn>, given a <a for=/>request</a> <var>request</var>
and a <a for=/>response</a> <var>response</var>, is to run these steps:

<ol>
<li><p>Let <var>mimeType</var> be the result of <a>extracting a MIME type</a> from
<var>response</var>'s <a for=response>header list</a>.

<li><p>Let <var>nosniff</var> be the result of <a>determining nosniff</a> given
<var>response</var>'s <a for=response>header list</a>.

<li>
<p>If <var>mimeType</var> is not failure, then:

<ol>
<li><p>If <var>mimeType</var> is an <a>opaque-response-safelisted MIME type</a>, then return
true.

<li><p>If <var>mimeType</var> is an <a>opaque-response-blocklisted-never-sniffed MIME type</a>,
then return false.

<li><p>If <var>response</var>'s <a for=response>status</a> is 206 and <var>mimeType</var> is an
<a>opaque-response-blocklisted MIME type</a>, then return false.

<li><p>If <var>nosniff</var> is true and <var>mimeType</var> is an
<a>opaque-response-blocklisted MIME type</a> or its <a for="MIME type">essence</a> is
"<code>text/plain</code>", then return false.
</ol>

<li><p>If <var>request</var>'s <a for=request>no-cors media request state</a> is
"<code>subsequent</code>", then return true.

<li><p>If <var>response</var>'s <a for=response>status</a> is 206 and
<a>validate a partial response</a> given 0 and <var>response</var> returns invalid, then return
false.
<!-- TODO Integrate https://wicg.github.io/background-fetch/#validate-a-partial-response into Fetch -->

<li><p>Wait for 1024 bytes of <var>response</var>'s <a for=response>body</a> or end-of-file,
whichever comes first and let <var>bytes</var> be those bytes.
<!-- TODO Obtaining these bytes needs to be defined in terms of a transform stream. -->

<li>
<p>If the <a>audio or video type pattern matching algorithm</a> given <var>bytes</var> does not
return undefined, then:

<ol>
<li><p>If <var>requests</var>'s <a for=request>no-cors media request state</a> is not
"<code>initial</code>", then return false.

<li><p>If <var>response</var>'s <a for=response>status</a> is not 200 or 206, then return false.

<li><p>Return true.
</ol>

<li><p>If <var>requests</var>'s <a for=request>no-cors media request state</a> is not
"<code>N/A</code>", then return false.

<li><p>If the <a>image type pattern matching algorithm</a> given <var>bytes</var> does not return
undefined, then return true.

<li>
<p>If <var>nosniff</var> is true, then return false.

<p class=note>This check is made late as unfortunately images and media are always sniffed.

<li><p>If <var>response</var>'s <a for=response>status</a> is not an <a>ok status</a>, then return
false.

<li>
<p>If <var>mimeType</var> is failure, then return true.

<p class=note>This could be improved at somewhat significant cost. See
<a href=https://github.com/annevk/orb/issues/28>annevk/orb #28</a>.

<li><p>If <var>mimeType</var>'s <a for="MIME type">essence</a> <a for=string>starts with</a>
"<code>audio/</code>", "<code>image/</code>", or "<code>video/</code>", then return false.

<li><p>Let <var>responseBodyBytes</var> be null.

<li>
<p>Let <var>processBody</var> given a <a for=/>byte sequence</a> <var>bytes</var> be these steps:

<ol>
<li><p>Set <var>responseBodyBytes</var> to <var>bytes</var>.

<li><p>Set <var>response</var>'s <a for=response>body</a> to the <a for="body with type">body</a>
of the result of <a for=BodyInit>safely extracting</a> <var>bytes</var>.
</ol>

<li><p>Let <var>processBodyError</var> be this step: set <var>responseBodyBytes</var> to failure.

<li><p><a>Fully read</a> <var>response</var>'s <a for=response>body</a> given <a>processBody</a>
and <var>processBodyError</var>.

<li><p>Wait for <var>responseBodyBytes</var> to be non-null.

<li><p>If <var>responseBodyBytes</var> is failure, then return false.

<li><p><a for=/>Assert</a>: <var>responseBodyBytes</var> is a <a for=/>byte sequence</a>.

<li><p>If <a>parse JSON bytes to a JavaScript value</a> given <var>responseBodyBytes</var> does not
throw, then return false. If it throws, catch the exception and ignore it.

<li><p>Let <var>sourceText</var> be the result of <a for=/>decoding</a>
<var>responseBodyBytes</var> given <var>request</var>'s
<a for=request>no-cors JavaScript fallback encoding</a>.

<li><p>If <a>ParseText</a>(<var>sourceText</var>, <a>Script</a>) returns a <a>Script Record</a>,
then return true.
<!-- Ideally HTML owns this so ECMAScript changes don't end up impacting Fetch. We could
potentially make this use "create a classic script" instead with some mock data. Maybe that is
better? -->

<li><p>Return false.
</ol>


<h4 id=orb-mime-type-sets>New MIME type sets</h4>

<p class=note>The definitions in this section are solely for the purpose of abstracting parts of the
<a>opaque-response-safelist check</a>. They are not suited for usage elsewhere.

<p>An <dfn>opaque-response-safelisted MIME type</dfn> is a <a>JavaScript MIME type</a> or a
<a for=/>MIME type</a> whose <a for="MIME type">essence</a> is "<code>text/css</code>" or
"<code>image/svg+xml</code>".

<p>An <dfn>opaque-response-blocklisted MIME type</dfn> is an <a>HTML MIME type</a>,
<a>JSON MIME type</a>, or <a>XML MIME type</a>.

<p>An <dfn>opaque-response-blocklisted-never-sniffed MIME type</dfn> is a <a for=/>MIME type</a>
whose <a for="MIME type">essence</a> is one of:

<ul class=brief>
<li>"<code>application/gzip</code>"
<li>"<code>application/msexcel</code>"
<li>"<code>application/mspowerpoint</code>"
<li>"<code>application/msword</code>"
<li>"<code>application/msword-template</code>"
<li>"<code>application/pdf</code>"
<li>"<code>application/vnd.ces-quickpoint</code>"
<li>"<code>application/vnd.ces-quicksheet</code>"
<li>"<code>application/vnd.ces-quickword</code>"
<li>"<code>application/vnd.ms-excel</code>"
<li>"<code>application/vnd.ms-excel.sheet.macroenabled.12</code>"
<li>"<code>application/vnd.ms-powerpoint</code>"
<li>"<code>application/vnd.ms-powerpoint.presentation.macroenabled.12</code>"
<li>"<code>application/vnd.ms-word</code>"
<li>"<code>application/vnd.ms-word.document.12</code>"
<li>"<code>application/vnd.ms-word.document.macroenabled.12</code>"
<li>"<code>application/vnd.msword</code>"
<li>"<code>application/vnd.openxmlformats-officedocument.presentationml.presentation</code>"
<li>"<code>application/vnd.openxmlformats-officedocument.presentationml.template</code>"
<li>"<code>application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</code>"
<li>"<code>application/vnd.openxmlformats-officedocument.spreadsheetml.template</code>"
<li>"<code>application/vnd.openxmlformats-officedocument.wordprocessingml.document</code>"
<li>"<code>application/vnd.openxmlformats-officedocument.wordprocessingml.template</code>"
<li>"<code>application/vnd.presentation-openxml</code>"
<li>"<code>application/vnd.presentation-openxmlm</code>"
<li>"<code>application/vnd.spreadsheet-openxml</code>"
<li>"<code>application/vnd.wordprocessing-openxml</code>"
<li>"<code>application/x-gzip</code>"
<li>"<code>application/x-protobuf</code>"
<li>"<code>application/x-protobuffer</code>"
<li>"<code>application/zip</code>"
<li>"<code>multipart/byteranges</code>"
<li>"<code>multipart/signed</code>"
<li>"<code>text/event-stream</code>"
<li>"<code>text/csv</code>"
</ul>



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

Expand Down Expand Up @@ -4105,8 +4313,14 @@ steps:

<li><p>Set <var>request</var>'s <a for=request>response tainting</a> to "<code>opaque</code>".

<li><p>Return the result of running <a>scheme fetch</a> given <var>fetchParams</var>.
<li><p>Let <var>opaqueResponse</var> be the result of running <a>scheme fetch</a> given
<var>fetchParams</var>.
<!-- file URLs end up here as they are not same-origin typically. -->

<li><p>If the <a>opaque-response-safelist check</a> given <var>request</var> and
<var>opaqueResponse</var> returns true, then return <var>opaqueResponse</var>.

<li><p>Return a <a>network error</a>.
</ol>

<dt><var>request</var>'s <a for=request>current URL</a>'s <a for=url>scheme</a> is not an
Expand Down Expand Up @@ -8370,6 +8584,7 @@ Mohamed Zergaoui,
Mohammed Zubair Ahmed<!-- M-ZubairAhmed; GitHub -->,
Moritz Kneilmann,
Ms2ger,
Nathan Froyd,
Nico Schlömer,
Nicolás Peña Moreno,
Nikhil Marathe,
Expand Down

0 comments on commit d4ab1db

Please sign in to comment.