Skip to content

Commit 54ae572

Browse files
authored
Use "least restrictive" directive instead of prefetch-src (#582)
When prefetching a resource (or preconnecting to an origin), the destination of the request is unknown and also not important. e.g. if this resource is a script and would be disallowed by `script-src`, the directive would be invoked again when the response is about to be consumed and would be rejected then. The only security measure valid for prefetch/preconnect is to avoid exfiltration - i.e. block the request when the default directive blocks this URL and no other directive allows it.
1 parent 1f4904f commit 54ae572

File tree

1 file changed

+77
-81
lines changed

1 file changed

+77
-81
lines changed

index.bs

+77-81
Original file line numberDiff line numberDiff line change
@@ -2140,6 +2140,14 @@ this algorithm returns normally if compilation is allowed, and throws a
21402140
out in more detail in the [[#should-block-request]] and
21412141
[[#should-block-response]] algorithms.
21422142

2143+
<div class="note">
2144+
Resource hints such as [^link/rel/prefetch^] and [^link/rel/dns-prefetch^] generate requests that
2145+
aren't tied to any specific [=fetch directive=], but are instead governed by the union of servers
2146+
allowed in all of a policy's directives' [=source lists=]. If <a>default-src</a> is not specified, these
2147+
requests will always be allowed. For more information, see [[#exfiltration]].
2148+
</div>
2149+
2150+
21432151
<div class="example">
21442152
The following header:
21452153

@@ -2156,7 +2164,6 @@ this algorithm returns normally if compilation is allowed, and throws a
21562164
<a>img-src</a> <a grammar>'self'</a>;
21572165
<a>manifest-src</a> <a grammar>'self'</a>;
21582166
<a>media-src</a> <a grammar>'self'</a>;
2159-
<a>prefetch-src</a> <a grammar>'self'</a>;
21602167
<a>object-src</a> <a grammar>'self'</a>;
21612168
<a>script-src-elem</a> <a grammar>'self'</a>;
21622169
<a>script-src-attr</a> <a grammar>'self'</a>;
@@ -2186,7 +2193,6 @@ this algorithm returns normally if compilation is allowed, and throws a
21862193
<a>img-src</a> <a grammar>'self'</a>;
21872194
<a>manifest-src</a> <a grammar>'self'</a>;
21882195
<a>media-src</a> <a grammar>'self'</a>;
2189-
<a>prefetch-src</a> <a grammar>'self'</a>;
21902196
<a>object-src</a> <a grammar>'self'</a>;
21912197
<a>script-src-elem</a> https://example.com;
21922198
<a>script-src-attr</a> <a grammar>'self'</a>;
@@ -2703,74 +2709,6 @@ this algorithm returns normally if compilation is allowed, and throws a
27032709

27042710
4. Return "`Allowed`".
27052711

2706-
<h4 id="directive-prefetch-src">`prefetch-src`</h4>
2707-
2708-
The <dfn export>prefetch-src</dfn> directive restricts the URLs from which resources may be
2709-
prefetched or prerendered. The syntax for the directive's name and value is described by the
2710-
following ABNF:
2711-
2712-
<pre>
2713-
directive-name = "prefetch-src"
2714-
directive-value = <a grammar>serialized-source-list</a>
2715-
</pre>
2716-
2717-
<div class="example">
2718-
Given a page with the following Content Security Policy:
2719-
2720-
<pre>
2721-
<a http-header>Content-Security-Policy</a>: <a>prefetch-src</a> https://example.com/
2722-
</pre>
2723-
2724-
Fetches for the following code will return network errors, as the URLs provided do not match
2725-
`prefetch-src`'s <a>source list</a>:
2726-
2727-
<pre highlight="html">
2728-
&lt;link rel="prefetch" src="https://example.org/"&gt;&lt;/link&gt;
2729-
&lt;link rel="prerender" src="https://example.org/"&gt;&lt;/link&gt;
2730-
</pre>
2731-
</div>
2732-
2733-
<h5 algorithm id="prefetch-src-pre-request">
2734-
`prefetch-src` Pre-request check
2735-
</h5>
2736-
2737-
This directive's <a for="directive">pre-request check</a> is as follows:
2738-
2739-
Given a <a for="/">request</a> (|request|) and a <a for="/">policy</a> (|policy|):
2740-
2741-
1. Let |name| be the result of executing [[#effective-directive-for-a-request]]
2742-
on |request|.
2743-
2744-
2. If the result of executing [[#should-directive-execute]] on |name|,
2745-
`prefetch-src` and |policy| is "`No`", return "`Allowed`".
2746-
2747-
3. If the result of executing [[#match-request-to-source-list]] on |request|,
2748-
this directive's [=directive/value=], and |policy|,
2749-
is "`Does Not Match`", return "`Blocked`".
2750-
2751-
4. Return "`Allowed`".
2752-
2753-
<h5 algorithm id="prefetch-src-post-request">
2754-
`prefetch-src` Post-request check
2755-
</h5>
2756-
2757-
This directive's <a for="directive">post-request check</a> is as follows:
2758-
2759-
Given a <a for="/">request</a> (|request|), a <a>response</a> (|response|), and a
2760-
<a for="/">policy</a> (|policy|):
2761-
2762-
1. Let |name| be the result of executing [[#effective-directive-for-a-request]]
2763-
on |request|.
2764-
2765-
2. If the result of executing [[#should-directive-execute]] on |name|,
2766-
`prefetch-src` and |policy| is "`No`", return "`Allowed`".
2767-
2768-
3. If the result of executing [[#match-response-to-source-list]] on |response|,
2769-
|request|, this directive's [=directive/value=], and |policy|,
2770-
is "`Does Not Match`", return "`Blocked`".
2771-
2772-
4. Return "`Allowed`".
2773-
27742712
<h4 id="directive-script-src">`script-src`</h4>
27752713

27762714
The <dfn export>script-src</dfn> directive restricts the locations from which scripts
@@ -3794,16 +3732,41 @@ this algorithm returns normally if compilation is allowed, and throws a
37943732
algorithm returns the violated <a>directive</a> if the request violates the
37953733
policy, and "`Does Not Violate`" otherwise.
37963734

3797-
1. Let |violates| be "`Does Not Violate`".
3735+
1. If |request|'s [=request/initiator=] is "`prefetch`", then return the result of executing
3736+
[[#does-resource-hint-violate-policy]] on |request| and |policy|.
3737+
3738+
2. Let |violates| be "`Does Not Violate`".
37983739

3799-
2. <a for=set>For each</a> |directive| of |policy|:
3740+
3. <a for=set>For each</a> |directive| of |policy|:
38003741

38013742
1. Let |result| be the result of executing |directive|'s
38023743
<a for="directive">pre-request check</a> on |request| and |policy|.
38033744

38043745
2. If |result| is "`Blocked`", then let |violates| be |directive|.
38053746

3806-
3. Return |violates|.
3747+
4. Return |violates|.
3748+
3749+
<h5 id="does-resource-hint-violate-policy">
3750+
Does resource hint |request| violate |policy|?
3751+
</h5>
3752+
3753+
Given a <a for="/">request</a> (|request|) and a <a for="/">policy</a> (|policy|), this
3754+
algorithm returns the default <a>directive</a> if the resource-hint request violates all the
3755+
policies, and "`Does Not Violate`" otherwise.
3756+
3757+
1. Let |defaultDirective| be |policy|'s first [=directive=] whose [=directive/name=] is
3758+
"`default-src`".
3759+
3760+
2. If |defaultDirective| does not exist, return "`Does Not Violate`".
3761+
3762+
3. <a for=set>For each</a> |directive| of |policy|:
3763+
3764+
1. Let |result| be the result of executing |directive|'s
3765+
<a for="directive">pre-request check</a> on |request| and |policy|.
3766+
3767+
2. If |result| is "`Allowed`", then return "`Does Not Violate`".
3768+
3769+
4. Return "`Blocked`".
38073770

38083771
<h5 id="match-nonce-to-source-list" algorithm>
38093772
Does |nonce| match |source list|?
@@ -4333,11 +4296,11 @@ this algorithm returns normally if compilation is allowed, and throws a
43334296
`null` or the <a for="directive">name</a> of the request's
43344297
<dfn for="request" export>effective directive</dfn>:
43354298

4336-
1. If |request|'s <a for="request">initiator</a> is "`fetch`" or its
4337-
<a for="request">destination</a> is "", return `connect-src`.
4299+
1. If |request|'s [=request/initiator=] is "`prefetch`" or "`prerender`",
4300+
return `default-src`.
43384301

4339-
2. If |request|'s [=request/initiator=] is "`prefetch`" or "`prerender`",
4340-
return `prefetch-src`.
4302+
2. If |request|'s <a for="request">initiator</a> is "`fetch`" or its
4303+
<a for="request">destination</a> is "", return `connect-src`.
43414304

43424305
3. Switch on |request|'s <a for="request">destination</a>, and execute
43434306
the associated steps:
@@ -4460,10 +4423,6 @@ this algorithm returns normally if compilation is allowed, and throws a
44604423
::
44614424
1. Return `<< "manifest-src", "default-src" >>`.
44624425

4463-
: "`prefetch-src`"
4464-
::
4465-
1. Return `<< "prefetch-src", "default-src" >>`.
4466-
44674426
: "`object-src`"
44684427
::
44694428
1. Return `<< "object-src", "default-src" >>`.
@@ -5015,8 +4974,45 @@ this algorithm returns normally if compilation is allowed, and throws a
50154974
</pre>
50164975
</div>
50174976
</section>
4977+
<section>
4978+
<h3 id="exfiltration">
4979+
Exfiltration
4980+
</h3>
4981+
4982+
Data exfiltration can occur when the contents of the request, such as the URL, contain
4983+
information about the user or page that should be restricted and not shared.
4984+
4985+
Content Security Policy can mitigate data exfiltration if used to create allowlists of servers
4986+
with which a page is allowed to communicate. Note that a policy which lacks the <a>default-src</a>
4987+
directive cannot mitigate exfiltration, as there are kinds of requests that are not addressable
4988+
through a more-specific directive ([^link/rel/prefetch^], for example).
4989+
4990+
<div class="example">
4991+
In the following example, a policy with draconian restrictions on images, fonts, and scripts
4992+
can still allow data exfiltration via other request types (`fetch()`, [^link/rel/prefetch^], etc):
4993+
4994+
<pre>
4995+
<a http-header>Content-Security-Policy</a>: img-src 'none' script-src 'none' font-src 'none'
4996+
</pre>
4997+
4998+
Supplementing this policy with `default-src 'none'` would improve the page's robustness
4999+
against this kind of attack.
5000+
</div>
5001+
5002+
<div class="example">
5003+
In the following example, the <a>default-src</a> directive appears to protect from
5004+
exfiltration, however the <a>img-src</a> directive relaxes this restriction by using a
5005+
wildcard, which allows data exfiltration to arbitrary endpoints. A policy's exfiltration
5006+
mitigation ability depends upon the least-restrictive directive allowlist:
5007+
5008+
<pre>
5009+
<a http-header>Content-Security-Policy</a>: default-src 'none'; img-src *
5010+
</pre>
5011+
</div>
50185012
</section>
50195013

5014+
5015+
50205016
<section>
50215017
<h2 id="implementation-considerations">Implementation Considerations</h2>
50225018

0 commit comments

Comments
 (0)