Skip to content

Commit 19c98a2

Browse files
authored
Hash reporting for scripts (#693)
* Rough sketch of CSP report-hash * Spec builds * Add a type and tighten the language * Append the hash algorithm to the report * Change to a report-sha256 keyword * Improve definitions * move logic to post-request * Address more review comments * Review nits * Add an example * URL ref * typo * fix example * Fix example again * Review comments * Only apply to Window * Add destination * review comments
1 parent a2c0141 commit 19c98a2

File tree

1 file changed

+103
-7
lines changed

1 file changed

+103
-7
lines changed

index.bs

+103-7
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ Markup Shorthands: css off, markdown on
2222
At Risk: The [[#is-element-nonceable]] algorithm.
2323
</pre>
2424
<pre class="link-defaults">
25-
spec:dom; type:interface; text:Document
25+
spec:dom;
26+
type: interface
27+
text: Document
28+
type: dfn
29+
text: URL; url: https://dom.spec.whatwg.org/#dom-document-url
2630
spec:html
2731
type: dfn
2832
text: fallback base url
@@ -164,6 +168,11 @@ spec: WebRTC; urlPrefix: https://www.w3.org/TR/webrtc/
164168
type:dfn
165169
text: administratively-prohibited; url: #dfn-administratively-prohibited
166170

171+
spec:SRI; urlPrefix: https://w3c.github.io/webappsec-subresource-integrity
172+
type:dfn;
173+
text:applying algorithm to bytes; url: #apply-algorithm-to-response
174+
text: cryptographic hash function; url: #hash-functions
175+
167176
</pre>
168177
<pre class="biblio">
169178
{
@@ -182,7 +191,7 @@ spec: WebRTC; urlPrefix: https://www.w3.org/TR/webrtc/
182191
"REPORTING": {
183192
"href": "https://wicg.github.io/reporting/",
184193
"title": "Reporting API",
185-
"authors": [ "Ilya Gregorik", "Mike West" ]
194+
"authors": [ "Ilya Grigorik", "Mike West" ]
186195
},
187196
"TIMING": {
188197
"href": "https://owasp.org/www-pdf-archive/HackPra_Allstars-Browser_Timing_Attacks_-_Paul_Stone.pdf",
@@ -682,9 +691,10 @@ spec: WebRTC; urlPrefix: https://www.w3.org/TR/webrtc/
682691

683692
; Keywords:
684693
<dfn>keyword-source</dfn> = "<dfn>'self'</dfn>" / "<dfn>'unsafe-inline'</dfn>" / "<dfn>'unsafe-eval'</dfn>"
685-
/ "<dfn>'strict-dynamic'</dfn>" / "<dfn>'unsafe-hashes'</dfn>" /
694+
/ "<dfn>'strict-dynamic'</dfn>" / "<dfn>'unsafe-hashes'</dfn>"
686695
/ "<dfn>'report-sample'</dfn>" / "<dfn>'unsafe-allow-redirects'</dfn>"
687-
/ "<dfn>'wasm-unsafe-eval'</dfn>"
696+
/ "<dfn>'wasm-unsafe-eval'</dfn>" / "<dfn>'report-sha256'</dfn>"
697+
/ "<dfn>'report-sha384'</dfn>" / "<dfn>'report-sha512'</dfn>"
688698

689699
ISSUE: Bikeshed `unsafe-allow-redirects`.
690700

@@ -1089,6 +1099,46 @@ spec: WebRTC; urlPrefix: https://www.w3.org/TR/webrtc/
10891099

10901100
4. Return |result|.
10911101

1102+
<h4 id="potentially-report-hash" algorithm dfn export>Potentially report hash</h4>
1103+
1104+
Given a [=response=] |response|, a [=/request=] |request|, a [=directive=] |directive| and a
1105+
[=content security policy object=] |policy|, run the following steps:
1106+
1107+
1. Let |algorithm| be the empty [=string=].
1108+
1. If |directive|'s <a for="directive">value</a> <a for="list">contains</a> the
1109+
expression "<a grammar>`'report-sha256'`</a>", set |algorithm| to "sha256".
1110+
1. If |directive|'s <a for="directive">value</a> <a for="list">contains</a> the
1111+
expression "<a grammar>`'report-sha384'`</a>", set |algorithm| to "sha384".
1112+
1. If |directive|'s <a for="directive">value</a> <a for="list">contains</a> the
1113+
expression "<a grammar>`'report-sha512'`</a>", set |algorithm| to "sha512".
1114+
1. If |algorithm| is the empty [=string=], return.
1115+
1. Let |hash| be the empty [=string=].
1116+
1. If |response| is [=CORS-same-origin=], then:
1117+
1. Let |hash list| be a [=list=] of [=strings=], initially empty.
1118+
1. [=list/Append=] |algorithm| to |hash list|.
1119+
1. [=list/Append=] the result of [=applying algorithm to bytes=] on |response|'s
1120+
[=response/body=] and |algorithm| to |hash list|.
1121+
1. Let |hash| be the result of [=concatenating=] |hash list| with U+002D (-).
1122+
1. Let |global| be the |request|'s [=request/client=]'s [=/global object=].
1123+
1. If |global| is not a {{Window}}, return.
1124+
1. Let |stripped document URL| to be the result of executing [[#strip-url-for-use-in-reports]]
1125+
on |global|'s [=document=]'s [=Document/URL=].
1126+
1. If |policy|'s [=directive set=] does not contain a [=directive=] named "report-to", return.
1127+
1. Let |report-to directive| be a [=directive=] named "report-to" from |policy|'s [=directive
1128+
set=].
1129+
1. Let |body| be a [=csp hash report body=] with |stripped document URL| as its [=documentURL=],
1130+
|request|'s URL as its [=subresourceURL=], |hash| as its [=hash=], |request|'s
1131+
[=request/destination=] as its [=csp hash report body/destination=], and "subresource" as its
1132+
[=csp hash report body/type=].
1133+
1. [=Generate and queue a report=] with the following arguments:
1134+
: <var ignore>context</var>
1135+
:: <var ignore>settings object</var>
1136+
: <var ignore>type</var>
1137+
:: "csp-hash"
1138+
: <var ignore>destination</var>
1139+
:: |report-to directive|'s [=directive/value=].
1140+
: <var ignore>data</var>
1141+
:: |body|
10921142

10931143
<h3 id="html-integration">
10941144
Integration with HTML
@@ -1593,6 +1643,50 @@ this algorithm returns normally if compilation is allowed, and throws a
15931643
};
15941644
</pre>
15951645

1646+
When a directive that impacts [=script-like=] [=request/destinations=] has a `report-sha256`,
1647+
`report-sha384` or `report-sha512` value, and a [=/request=] with a [=script-like=]
1648+
[=request/destination=] is fetched, a <dfn export>csp hash report</dfn> will be generated and
1649+
sent out to a reporting endpoint associated with the <a for="/">policy</a>.
1650+
1651+
<p><a>csp hash reports</a> have the <a>report type</a> "csp-hash".</p>
1652+
1653+
<p><a>csp hash reports</a> are not <a>visible to <code>ReportingObserver</code>s</a>.
1654+
1655+
<p>A <dfn>csp hash report body</dfn> is a [=struct=] with the following fields:
1656+
<dfn for="csp hash report body">documentURL</dfn>,
1657+
<dfn for="csp hash report body">subresourceURL</dfn>,
1658+
<dfn for="csp hash report body">hash</dfn>,
1659+
<dfn for="csp hash report body">destination</dfn>,
1660+
<dfn for="csp hash report body">type</dfn>.
1661+
1662+
<div class="example">
1663+
When a document's response contains the headers:
1664+
```http
1665+
Reporting-Endpoints: hashes-endpoint="https://example.com/reports"
1666+
Content-Security-Policy: script-src 'self' 'report-sha256'; report-to hashes-endpoint
1667+
```
1668+
and the document loads the script "main.js", a report similar to the following one will be sent:
1669+
```http
1670+
POST /reports HTTP/1.1
1671+
Host: example.com
1672+
...
1673+
Content-Type: application/reports+json
1674+
1675+
[{
1676+
"type": "csp-hash-report",
1677+
"age": 12,
1678+
"url": "https://example.com/",
1679+
"user_agent": "Mozilla/5.0 (X11; Linux i686; rv:132.0) Gecko/20100101 Firefox/132.0",
1680+
"body": {
1681+
"document_url": "https://example.com/",
1682+
"subresource_url": "https://example.com/main.js",
1683+
"hash": "sha256-badbeef",
1684+
"type": "subresource",
1685+
"destination": "script"
1686+
}
1687+
}]
1688+
```
1689+
</div>
15961690
<h3 id="violation-events">
15971691
Violation DOM Events
15981692
</h3>
@@ -3702,25 +3796,27 @@ this algorithm returns normally if compilation is allowed, and throws a
37023796

37033797
1. If |request|'s <a for="request">destination</a> is <a for="request/destination">script-like</a>:
37043798

3799+
1. Call [=potentially report hash=] with |response|, |request|, |directive| and |policy|.
3800+
37053801
1. If the result of executing [[#match-nonce-to-source-list]] on
37063802
|request|'s <a for="request">cryptographic nonce metadata</a> and this
37073803
directive's <a for="directive">value</a> is "`Matches`", return
37083804
"`Allowed`".
37093805

3710-
2. If the result of executing
3806+
1. If the result of executing
37113807
[[#match-integrity-metadata-to-source-list]] on |request|'s <a
37123808
for="request">integrity metadata</a> and this directive's <a
37133809
for="directive">value</a> is "`Matches`", return "`Allowed`".
37143810

3715-
3. If |directive|'s <a for="directive">value</a> contains
3811+
1. If |directive|'s <a for="directive">value</a> contains
37163812
"<a grammar>`'strict-dynamic'`</a>":
37173813

37183814
1. If |request|'s <a for="request">parser metadata</a> is not
37193815
<a>"parser-inserted"</a>, return "`Allowed`".
37203816

37213817
Otherwise, return "`Blocked`".
37223818

3723-
4. If the result of executing [[#match-response-to-source-list]] on
3819+
1. If the result of executing [[#match-response-to-source-list]] on
37243820
|response|, |request|, |directive|'s <a for="directive">value</a>,
37253821
and |policy|, is "`Does Not Match`", return "`Blocked`".
37263822

0 commit comments

Comments
 (0)