From 5e1a1966598b14925182f8f35eaf925b9ca2509b Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Fri, 21 Feb 2020 17:51:44 -0500 Subject: [PATCH] Re-do algorithms to explicitly grab from the response Previously, the Fetch integration would "update" the origin's origin policy, i.e. make sure the version in the cache was updated. Then the various integration points (so far, CSP and FP) would grab the origin policy from the cache. This architecture is fragile (see discussion in #73) and does not match how implementations would reasonably work. This new version stores the origin policy on the response, and then uses that when constructing CSP and FP from the response. Fixes #73. --- index.src.html | 71 ++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/index.src.html b/index.src.html index 3d8ca76..5b0d7f8 100644 --- a/index.src.html +++ b/index.src.html @@ -66,6 +66,10 @@ text: feature policy directive; url: policy-directive text: parsing a feature policy directive; url: parse-policy-directive text: create a feature policy for a browsing context; url: create-for-browsingcontext + text: create a feature policy from a response; url: create-from-response + text: process response policy; url: process-response-policy; for: feature policy + text: merge directive with declared policy; url: merge-directive-with-declared-policy; for: feature policy + text: parse header from value and origin; url: parse-header-from-value-and-origin; for: feature policy spec: CSP; type: dfn; urlPrefix: https://w3c.github.io/webappsec-csp/ text: set response's CSP list; url: set-response-csp-list spec: RFC6797; urlPrefix: https://tools.ietf.org/html/rfc6797 @@ -440,14 +444,14 @@

Response processing

This section details the entry point algorithms, for determining which origin policy applies to an incoming [=response=].
- To update the origin policy from a response, given a [=response=] |response| and an [=environment settings object=] |client|: + To get the origin policy for a response, given a [=response=] |response| and an [=environment settings object=] |client|: - 1. If |response|'s [=response/URL=] is null, then return "success". - 1. Let |header| be the result of [=header list/getting=] `Origin-Policy` from |response|'s [=response/header list=]. - 1. If |header| is null, then return "success". - 1. Let (|allowedIds|, |preferredId|) be the result of parsing an `Origin-Policy` header given |header|. If this instead returns "unparseable", then return "success". + 1. If |response|'s [=response/URL=] is null, then return the [=null policy=]. 1. Let |origin| be |response|'s [=response/URL=]'s [=url/origin=]. - 1. Return the result of [=updating an origin's origin policy=] for |origin| given |client|, |allowedIds|, and |preferredId|. + 1. Let |header| be the result of [=header list/getting=] `Origin-Policy` from |response|'s [=response/header list=]. + 1. If |header| is null, then return the result of [=retrieving the cached origin policy=] for |origin| given |client|. + 1. Let (|allowedIds|, |preferredId|) be the result of parsing an `Origin-Policy` header given |header|. If this instead returns "unparseable", then return the [=null policy=]. + 1. Return the result of [=fetching an origin's origin policy=] for |origin| given |client|, |allowedIds|, and |preferredId|.
@@ -479,26 +483,26 @@

Response processing

Updating the origin policy

- To update an origin's origin policy for an [=/origin=] |origin| given |client|, |allowedIds|, and |preferredId|: + To fetch an origin's origin policy for an [=/origin=] |origin| given |client|, |allowedIds|, and |preferredId|: - 1. Let |cachedPolicy| be the result of [=retrieving the cached origin policy=] for |origin|. - 1. If |preferredId| is a string, and is [=list/contained=] in |cachedPolicy|'s [=origin policy/IDs=], then return "success". + 1. Let |cachedPolicy| be the result of [=retrieving the cached origin policy=] given |origin| and |client|. + 1. If |preferredId| is a string, and is [=list/contained=] in |cachedPolicy|'s [=origin policy/IDs=], then return |cachedPolicy|. 1. Let |url| be the result of [=getting the origin policy manifest URL=] for |origin|. - 1. If |url| is null, then return "success". + 1. If |url| is null, then return the [=null policy=]. 1. Let |networkRequest| be a new [=request=] whose [=request/url=] is |url|, [=request/client=] is |client|, [=request/service-workers mode=] is "none", [=request/destination=] is "manifest", [=request/mode=] is "same-origin", [=request/redirect mode=] is "error", [=request/credentials mode=] is "omit", [=request/referrer policy=] is "no-referrer", and [=request/cache mode=] is "no-cache". 1. If |allowedIds| [=list/contains=] one of |cachedPolicy|'s [=origin policy/IDs=], or if |allowedIds| [=list/contains=] [=latest=], then: 1. If |cachedPolicy| is the [=null policy=], or |preferredId| is not null, then [=in parallel=], [=fetch=] |networkRequest|. (This will update the cache, but the [=response=] will not be used.) - 1. Return "success". + 1. Return |cachedPolicy|. 1. If |allowedIds| contains null, then: 1. If |preferredId| is not null, then [=in parallel=], [=fetch=] |networkRequest|. (This will update the cache, but the [=response=] will not be used.) - 1. Return "success". + 1. Return the [=null policy=]. 1. Let |networkResponse| be the result of [=fetching=] |networkRequest|. (Unlike the [=in parallel=] fetches, this is blocking.) 1. Let |networkPolicy| be the result of [=getting an origin policy from a manifest response=] given |networkResponse|. 1. If any of the following is true: * |preferredId| is [=latest-from-network=]; or * |preferredId| is a string, and is [=list/contained=] in |networkPolicy|'s [=origin policy/IDs=]; or * |allowedIds| [=list/contains=] one of |networkPolicy|'s [=origin policy/IDs=], - then return "success". + then return |networkPolicy|. 1. Return "failure".

Although this specification uses the full [=fetching=] mechanism to check the HTTP cache for an origin policy, and then [=getting an origin policy from a manifest response|re-parses=] the result each time, implementations could use a more efficient mechanism, as long as the results are observably equivalent. In such cases, implementations ought to take particular care around respecting the cache expiration and keying semantics.

@@ -523,15 +527,13 @@

Updating the origin policy

- To retrieve the cached origin policy for an [=/origin=] |origin|: + To retrieve the cached origin policy for an [=/origin=] |origin| given an [=environment settings object=] |client|: 1. Let |url| be the result of [=getting the origin policy manifest URL=] for |origin|. 1. If |url| is null, return the [=null policy=]. - 1. Let |cacheCheckRequest| be a new [=request=] whose [=request/url=] is |url|, [=request/client=] is null, [=request/service-workers mode=] is "none", [=request/destination=] is "manifest", [=request/mode=] is "same-origin", [=request/redirect mode=] is "error", [=request/credentials mode=] is "omit", and [=request/cache mode=] is "only-if-cached". + 1. Let |cacheCheckRequest| be a new [=request=] whose [=request/url=] is |url|, [=request/client=] is |client|, [=request/service-workers mode=] is "none", [=request/destination=] is "manifest", [=request/mode=] is "same-origin", [=request/redirect mode=] is "error", [=request/credentials mode=] is "omit", and [=request/cache mode=] is "only-if-cached". 1. Let |cachedResponse| be the result of [=fetching=] |cacheCheckRequest|. 1. Return the result of [=getting an origin policy from a manifest response=] given |cachedResponse|. - - We define the origin policy for a given [=/origin=] to be the result of [=retrieving the cached origin policy=] for that origin.
@@ -586,33 +588,34 @@

Patches to other specifications

Fetch

-The HTTP-network fetch algorithm is modified by inserting a step after step 5, i.e. after request body streaming has begun but before response body streaming begins. This must call [=update the origin policy from a response=] given the response and the request's [=request/client=]. (This could cause the algorithm to block while it fetches an origin policy, if required do to so by the `Origin-Policy` header.) - -

Feature Policy

+Every [=response=] gets an associated origin policy, which is an [=/origin policy=]. It is initially the [=null origin policy=]. -The [=create a feature policy for a browsing context=] algorithm is modified by modifying the returned [=feature policy=]'s declared policy to be a [=map/clone=] of origin's [=origin/origin policy=]'s [=origin policy/feature policy=]. +The main fetch algorithm is modified by inserting the following step after step 10. (This is after headers have been received and much of the other preliminaries processed, but the request body is likely still downloading.) -

This means any allowlists provided in the `Feature-Policy` header will override those provided by the origin policy.

+1. Let |originPolicy| be the result of [=getting the origin policy for a response=] given |internalResponse| and request's [=request/client=]. +1. If |originPolicy| is "failure", then set response and |internalResponse| to a [=network error=]. +1. Otherwise, set |internalResponse|'s [=response/origin policy=] to |originPolicy|. -

Content Security Policy

+

Feature Policy

-The two main substantive steps of the [=set response's CSP list=] algorithm are modified from +The [=feature policy/process response policy=] and algorithm needs to be replaced with the following, given a [=response=] |response| and [=/origin=] |origin|: -
- 2. Let |policies| be the result of parsing the result of [=extracting header list values=] given `Content-Security-Policy` and |response|'s [=response/header list=], with a [=policy/source=] of "header", and a [=policy/disposition=] of "enforce". +1. Let |policy| be a [=list/clone=] of |response|'s [=response/origin policy=]'s [=origin policy/feature policy=]. +1. Let |header| be the concatenation of the [=header/values=] of all [=header=] fields in |response|'s [=response/header list=] whose name is `Feature-Policy`, separated by U+002C (,) (according to [RFC7230, 3.2.2]). +1. For each |element| returned by splitting |header| on commas: + 1. Let |directive| be the result of [=parsing a feature policy directive=] given |element| and |origin|. + 1. Perform [=feature policy/merge directive with declared policy=] given |directive| and |policy|. +1. Return |policy|. - 3. [=list/Append=] to |policies| the result of parsing the result of [=extracting header list values=] given `Content-Security-Policy-Report-Only` and |response|'s [=response/header list=], with a [=policy/source=] of "header", and a [=policy/disposition=] of "report". -
+

The [=feature policy/parse header from value and origin=] sub-algorithm is no longer necessary; its contents were inlined into the above rewrite.

-to +

Content Security Policy

-
- 2. Let |policies| be a [=list/clone=] of |response|'s [=response/URL=]'s [=url/origin=]'s [=origin/origin policy=]'s [=origin policy/content security policies=]. +The set response's CSP list algorithm needs its step 1 replaced with the following: - 3. [=list/Append=] to |policies| the result of parsing the result of [=extracting header list values=] given `Content-Security-Policy` and |response|'s [=response/header list=], with a [=policy/source=] of "header", and a [=policy/disposition=] of "enforce". +1. Set |response|'s [=response/CSP list=] to a [=list/clone=] of |response|'s [=response/origin policy=]'s [=origin policy/content security policies=]. - 4. [=list/Append=] to |policies| the result of parsing the result of [=extracting header list values=] given `Content-Security-Policy-Report-Only` and |response|'s [=response/header list=], with a [=policy/source=] of "header", and a [=policy/disposition=] of "report". -
+(The rest of the algorithm will then append to this list.)

Privacy and security considerations