Origin Policy

A Collection of Interesting Ideas,

This version:
https://WICG.github.io/origin-policy
Editor:
(Google Inc.)
Participate:
Discuss in the WICG. File an issue (open issues)

Abstract

This specification defines a delivery mechanism for a number of policies which are to be applied to an entire origin. It compliments header-based delivery mechanisms for existing policies (Content Security Policy, Referrer Policy, etc).

1. Introduction

Developers set a number of properties associated with resources on an origin by delivering resource-specific HTTP response headers and meta elements. This is becoming more common over time, and it’s quite normal these days to see multiple kilobytes at the beginning of every response dedicated to such metadata. Setting this metadata is valuable indeed, as it can have a large impact on performance, security, and privacy.

However, the existing delivery mechanism is ill-suited to the task, suffering from a clear mismatch between the resource-specific nature of the metadata declarations on the one hand, and the origin-wide intent of the metadata on the other. Take Strict-Transport-Security [RFC6797] and Public-Key-Pins [RFC7469], for example, which explicitly alter the state of an entire origin, but are delivered in resource-specific response headers.

Moreover, many resource-specific headers are deployed in such a way as to be de facto static across all resources that an origin serves. Content-Security-Policy [CSP3], for instance, can be very granular indeed, but is commonly implemented by setting a single policy which is delivered for an entire application.

A number of implications follow:

  1. Servers are required to repeat themselves. At length. Content-Security-Policy alone can easily eat multiple kilobytes of each navigational response, bandwidth which could instead be dedicated to content a user might care about. This has obvious and direct impact on the delay a user experiences when navigating, but has less obvious knock-on effects that reduce performance further. HTTP/2’s HPACK [RFC7541] header compression is limited to ~4k of state for processing, for instance, meaning that these verbose headers can greatly reduce its effectiveness

  2. Servers are required to repeat themselves. Unerringly. When policy applies strictly to a resource, and not to the origin, then a server must send that policy down with every response. If the developer forgets a page (which is actually a very common occurence: when’s the last time you thought about your 404 error page? How about your 417? Or 505?), then the policy’s protections don’t apply to that resource, leaving an exploitable hole.

This document introduces a new delivery mechanism for policies which are meant to apply to an entire origin. In short, a server will provide an Origin Policy Manifest file at a well-known location. This file contains all of the metadata the server would like to set for each response. User agents can be instructed to synchronously download and process this manifest before completing a navigation to an origin’s resources, ensuring that the policy contained therin will be safely applied to each resource, and allowing the server to skip the overhead of including the relevant headers with each response. Typically, the server can speed things up even more by using HTTP/2 Server Push ([RFC7540], section 8.2) to proactively send the manifest file along with the response to the user agent’s first request.

1.1. Examples

MegaCorp, Inc. wishes to ensure that a baseline content security policy is applied to each of the pages on https://example.com, while avoiding the overhead associated with large response headers, and the uncertainty that they’ve really covered everything that lives on the origin.

When they see a request come in that contains a Sec-Origin-Policy header, they can respond in kind, pointing the client to a manifest file in a well-known location on their server. That is, given the following request:

GET / HTTP/1.1
Host: example.com
Connection: keep-alive
...
Sec-Origin-Policy: 1
...

MegaCorp, Inc. can respond with:

HTTP/1.1 200 OK
Content-Encoding: gzip
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html
...
Sec-Origin-Policy: "policy-1"
Vary: sec-origin-policy
...

The client will parse the response headers, and synchronously request https://example.com/.well-known/origin-policy/policy-1 before completing the navigation. The policies contained in that file will be cached according to the normal HTTP caching rules, and applied to pages on https://example.com/ (including the current navigation).

{
  "headers": [
    {
      "name": "Content-Security-Policy",
      "value": "script-src 'self' https://cdn.example.com",
      "type": "fallback"
    },
    {
      "name": "Referrer-Policy",
      "value": "origin-when-cross-origin",
      "type": "fallback"
    }
    {
      "name": "Content-Security-Policy",
      "value": "object-src 'none'; frame-ancestors 'none'",
      "type": "baseline"
    },
    {
      "name": "Strict-Transport-Security",
      "value": "max-age=10886400; includeSubDomains; preload",
      "type": "baseline"
    },
    {
      "name": "X-Content-Type-Options",
      "value": "nosniff",
      "type": "baseline"
    }
  ],
  "cors-preflight": {
    "origins": "*"
  }
}

Subsequent requests from the same client will contain the version of the policy currently cached for the origin (with a slight caveat: see §6.1 Tracking). In this case:

GET / HTTP/1.1
Host: example.com
Connection: keep-alive
...
Sec-Origin-Policy: "policy-1"
...

Note: MegaCorp, Inc. can signficantly speed up this interaction by proactively pushing the current manifest down to the client along with the initial response. See §5.4 Server Push for details.

MegaCorp, Inc. wishes to update the origin policy it has distributed to clients. It can do so by setting an appropriate Sec-Origin-Policy header in the response. That is, given the following request:
GET / HTTP/1.1
Host: example.com
Connection: keep-alive
...
Sec-Origin-Policy: "policy-1"
...

MegaCorp, Inc. can respond with:

HTTP/1.1 200 OK
Content-Encoding: gzip
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html
...
Sec-Origin-Policy: "policy-2"
Vary: sec-origin-policy
...

The client will process the header, replace the policy-1 manifest it downloaded in the previous example with the newly specified policy-2 manifest, and apply the latter to the response.

MegaCorp, Inc. wishes to remove the origin policy it has distributed to clients. It can do so by setting an Sec-Origin-Policy header with a value of 0 in the response:
HTTP/1.1 200 OK
Content-Encoding: gzip
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html
...
Sec-Origin-Policy: 0
...

The client will process the header, and evict any origin policy for the response’s origin.

Note: The manifest itself will be removed, but some of the policies it set may still be effective. For example, if the Strict-Transport-Security header was set, the maxage property it contained may still force TLS on any connection to the origin’s host. Those kinds of behaviors will need to be cleared individually, as appropriate for the feature. For example, to remove HSTS, the server could send Strict-Transport-Security: maxage=0.

2. Framework

2.1. Concepts

An origin policy object is a tuple of a string (version), and the contents of an origin policy manifest (body).

User agents have a Origin Policy Store, which is a key/value store that constitutes a 1:1 mapping between origin policy objects and origins.

When asked to retrieve an Origin Policy for an origin, an Origin Policy Store will return either a single origin policy object (if one is stored for the given origin), or null (if nothing is stored for the given origin).

When asked to evict an Origin policy for an origin, an Origin Policy Store will discard any origin policy object associated with the given origin.

2.2. The Sec-Origin-Policy HTTP Header Field

The Sec-Origin-Policy HTTP request header field is sent with navigational HTTP requests in order to advertise support generally for the origin policy manifest mechanism defined in this document, and to inform the server which version of its origin policy is cached locally. Its value MUST be either 1 of a base64-encoded value, wrapped in double-quotes.

The same header is sent by the server in responses to such requests to inform the client that a origin policy manifest is available to fetch. Its value MUST be either 0 or a base64-encoded value, wrapped in double-quotes. The following ABNF defines that more formally:

Sec-Origin-Policy: 0 / 1 / manifest-name
manifest-name: DQUOTE base64-value DQUOTE
; base64-value is defined in [[!CSP3]]
; DQUOTE is defined in [[!RFC5234]]

This header MUST be ignored if Secure Contexts §is-url-trustworthy returns "Not Trustworthy" when executed upon the response’s URL.

2.3. The Origin Policy Manifest File

The Origin Policy Manifest is a JSON-formatted document consisting of a top-level object with one or more members. Each member defines a specific aspect of an origin’s policy.

{
  "headers": [
    {
      "name": "Content-Security-Policy",
      "value": "script-src 'self' https://cdn.example.com",
      "type": "fallback"
    },
    {
      "name": "Referrer-Policy",
      "value": "origin-when-cross-origin",
      "type": "fallback"
    }
    {
      "name": "Content-Security-Policy",
      "value": "object-src 'none'; frame-ancestors 'none'",
      "type": "baseline"
    },
    {
      "name": "Strict-Transport-Security",
      "value": "max-age=10886400; includeSubDomains; preload",
      "type": "baseline"
    },
    {
      "name": "X-Content-Type-Options",
      "value": "nosniff",
      "type": "baseline"
    }
  ],
  "cors-preflight": {
    "origins": "*"
  }
}

2.3.1. The origin-policy well-known location

Origin Policy Manifest files for a given origin MUST be located as suffixes to the well-known location /.well-known/origin-policy [RFC5785]. Their names MUST be strings of characters matching the base64-value grammar. That is, /.well-known/origin-policy/a might point to one Origin Policy Manifest file, while /.well-known/origin-policy/b might point to another.

Servers MUST respond to a GET request to /.well-known/origin-policy with a 302 redirect whose Location header points to the origin’s current Origin Policy Manifest, or with a 404 response if no such policy is available.

Note: This redirect can be used as a discovery mechanism. User agents might prefetch such manifest files for origins a user is likely to visit, testing services might give developers automated feedback on their origin’s manifest, etc.

2.3.2. The headers member

The headers member defines a set of HTTP response headers which will be automatically appended to resources delivered from the manifest’s origin. The headers specified in this member’s value are categorized either as baseline headers which appended to every resource served by the origin, or as fallback headers which are appended only to those resources which don’t themselves deliver the header.

The headers member’s value MUST be a sequence of HTTPHeader dictionaries, each containing the following members:

name, of type USVString

A string representing the header’s name: "Content-Security-Policy", for example. This member is required.

value, of type USVString

A string representing the header’s value: "script-src 'none'", for example. This member is required.

type, of type HTTPHeaderType, defaulting to "baseline"

Either "baseline" or "fallback", designating this header as either a baseline header or fallback header respectively. If this member is not explicitly present, it defaults to "baseline".

Members other than those specified here will be ignored, and will not cause parse failures.

It may be a dangerous (or at least confusing) footgun to allow certain headers to be set on an origin (consider Content-Type or Content-Length, for instance). Perhaps it would be reasonable to limit the headers member to some whitelist of expected headers?

2.3.3. The cors-preflight and unsafe-cors-preflight-with-credentials members

The cors-preflight and unsafe-cors-preflight-with-credentials members assert that an origin understands the CORS protocol, and that the user agent can therefore assume success for certain types of CORS-preflight requests. The former governs uncredentialed requests, the latter credentialed.

Both members values MUST be a CORSOptions dictionary, with the following members:

origins, of type CORSOptionList

Either the string "*" or a sequence of strings, representing the origins for which CORS-preflight requests may be bypassed. This member is required.

If this is a member of unsafe-cors-preflight-with-credentials, then its value MUST not be "*".

methods, of type CORSOptionList, nullable

Either the string "*" or a sequence of strings, representing the non-CORS-safelisted method methods for which CORS-preflight request may be bypassed. This member is optional.

If this is a member of unsafe-cors-preflight-with-credentials, then its value MUST not be "*".

headers, of type CORSOptionList, nullable

Either the string "*" or a sequence of strings, representing the non-CORS-safelisted request-header headers which CORS-preflight request may be bypassed. This member is optional.

If this is a member of unsafe-cors-preflight-with-credentials, then its value MUST not be "*".

Note: This member does not set CORS headers for responses in general; it only allows the user agent to skip the OPTIONS preflight request for a defined set of requests.

2.3.3.1. Examples
MegaCorp, Inc. has a partnership with Excellent Examples, GmbH., and wishes to allow the latter to make otherwise unsafely credentialed cross-origin requests without the overhead of CORS preflight requests. They can do so with the following manifest:
{
  "unsafe-cors-preflight-with-credentials": {
    "origins": [ "https://excellent-example.test/" ]
  }
}

As spelled out in §3.4.4 Does Origin Policy bypass request’s CORS-preflight request?, this bypasses CORS-preflight requests for credentialed requests coming from https://excellent-example.text, as long as they don’t set strange headers, or use strange HTTP methods.

If the API required a special header (say X-Examples-Are-Excellent), or a custom HTTP method (say EXAMPLE), MegaCorp, Inc. could bypass preflights for those as well by changing their manifest as follows:

{
  "unsafe-cors-preflight-with-credentials": {
    "origins": [ "https://excellent-example.test/" ],
    "methods": [ "EXAMPLE" ],
    "headers": [ "X-Examples-Are-Excellent" ]
  }
}
MegaCorp, Inc. is a staunch advocate of open data, and wishes to make all of the API endpoints on https://open.example.com/ available for uncredentialed requests from any origin. They can do so with the following manifest:
{
  "headers": [
    {
      "name": "Access-Control-Allow-Origin",
      "value": "*",
      "type": "fallback"
    }
  ],
  "cors-preflight": {
    "origins": "*"
  }
}

As spelled out in §3.4.4 Does Origin Policy bypass request’s CORS-preflight request?, this bypasses CORS-preflight requests for uncredentialed requests from any origin. Further, it appends an Access-Control-Allow-Origin: * header to each response that doesn’t set a custom value for that header.

Note: This doesn’t allow anything more than the "Basic safe CORS protocol setup", as * will not allow credentialed access to any resource and the static manifest cannot be configured to echo the actual origin of the requestor.

2.3.4. Syntax

//
// header member:
//
enum HTTPHeaderType { "baseline", "fallback" };

dictionary HTTPHeader {
  required USVString name;
  required USVString value;
  HTTPHeaderType type = "baseline";
};


//
// cors-preflight and unsafe-cors-preflight-with-credentials
//
enum Wildcard { "*" };
typedef (Wildcard or sequence<USVString>) CORSOptionList;

dictionary CORSOptions {
  CORSOptionList origins;
  CORSOptionList? methods;
  CORSOptionList? headers;
};

WebIDL is almost certainly the wrong way to describe the way the Sec-Origin-Policy header’s value is constructed. Still, it makes writing the spec quite a bit simpler, so. We’ll come up with something better later. Possibly as part of the JSON HTTP Header Field encoding work ongoing in the HTTP WG.

3. Algorithms

3.1. Retrieve origin’s manifest version.

Given an origin (origin), this algorithm returns 0 if no manifest is present in the user agent’s Origin Policy Store, or a string representing the cached version otherwise.

  1. Let version be 0.

  2. If the user agent’s Origin Policy Store contains a tuple for origin, let version be that tuple’s first element.

  3. Return version.

3.2. Evict origin’s cached manifest.

Given an origin (origin), this algorithm removes any cached manifest from the user agent’s Origin Policy Store:

  1. Instruct the user agent’s Origin Policy Store to evict any Origin Policy object stored for origin.

3.3. HTML Integration

At the end of the navigation algorithm, we’ll do the following:

  1. If we have a policy for the origin to which we’ve navigated (or we’re inheriting the origin for an about:blank or about:srcdoc (or if we’re Firefox, data:...), then we apply the policy to the document as defined in §3.3.1 Apply Origin Policy to settings. (which I haven’t written yet.

At the end of the "run a worker" algorithm, we’ll do the same thing.

3.3.1. Apply Origin Policy to settings.

Write this algorithm.

3.4. Fetch Integration

During a navigation request, Fetch will hook into this specification in two ways:

  1. At the top of the basic fetch algorithm, Fetch will call §3.4.1 Prepare request with Origin Policy. in order to append Sec-Origin-Policy to the request’s header list with a value of 1 if no manifest is cached for its URL’s origin, or a value of the verion of the manifest that is cached (e.g. "policy-1").

  2. When a response is received during HTTP-network fetch algorithm, Fetch will call into §3.4.2 Process response for Origin Policy. to sift through its headers, and extract the Sec-Origin-Policy header. If present, and its value does not match the value of the locally cached manifest for the response’s origin, we’ll issue a synchronous request for a resource located on the request’s URL’s origin at /.well-known/origin-policy/{value}, as defined in §3.4.2 Process response for Origin Policy..

    The manifest request will fail in a draconian fashion: if §3.4.2 Process response for Origin Policy. returns "Failure", the response to the request which triggered the manifest request will be set to a network error.

    Should we allow service workers to intercept/set policy? Maybe only if they are scoped to the whole origin?

  3. Directly after Fetch calls into §3.4.2 Process response for Origin Policy., it will call into §3.4.3 Apply an Origin Policy to response. to apply any cached policy to the response.

  4. Before issuing a CORS-preflight request, Fetch will call into §3.4.4 Does Origin Policy bypass request’s CORS-preflight request? in order to determine whether the preflight is necessary. If that algorithm returns "Bypass", Fetch will not issue a preflight request, but instead proceed as though the preflight succeeded. Otherwise, the preflight will be issued as usual.

3.4.1. Prepare request with Origin Policy.

Given a request request, add appropriate headers to inform a server about a client’s active policy:

  1. If request is not a navigation request, or if Secure Contexts §is-url-trustworthy returns "Not Trustworthy" when executed upon request’s url, then abort these steps.

  2. If the user agent is configured to include cookies with request, then let version be the result of executing §3.1 Retrieve origin’s manifest version. on request’s URL’s origin.

    Otherwise, let version be 1 (see §6.1 Tracking).

  3. Append a header named Sec-Origin-Policy with a value of version to request’s header list.

3.4.2. Process response for Origin Policy.

Given a response (response), this algorithm will sift through its headers, and fetch or evict the relevant Origin Policy Manifest as follows:

  1. Let version be the result of parsing "Sec-Origin-Policy" in response’s header list.

  2. Let url be a copy of response’s URL.

  3. Return "No-op" if any of the following statements are true:

    1. version is null.

    2. Secure Contexts §is-url-trustworthy returns "Not Trustworthy" when executed upon url.

  4. Assert: url is not an opaque origin.

  5. Return "Failure" if version does not match the manifest-name grammar.

  6. If version is 0:

    1. Execute §3.2 Evict origin’s cached manifest. upon url’s origin.

    2. Return "Success".

  7. Replace url’s path with the concatenation of "/.well-known/origin-policy/" and version.

  8. Let request be a new request with the following properties:

    url

    url

    client

    null

    skip-service-worker flag

    Set

    destination

    "manifest"

    mode

    "same-origin"

    redirect mode

    "error"

  9. Let response be the result of performing an HTTP-network-or-cache fetch using request.

  10. If response’s status is an ok status:

    1. Let MIME type be the result of extracting a MIME type from response’s header list.

    2. If MIME type is not application/manifest+json, abort these steps and return "Failure".

    3. Wait for response’s body.

    4. Store the tuple (version, response’s body) in the user agent’s Origin Policy Store for response’s URL’s origin.

    5. Return "Success".

  11. Return "Failure"

3.4.3. Apply an Origin Policy to response.

Given a response (response), the following algorithm applies the relevant origin policy object:

  1. Let origin be response’s URL’s origin.

  2. If origin is an opaque origin, or if Secure Contexts §is-origin-trustworthy returns "Not Trustworthy" when executed upon origin, abort these steps and return.

  3. Let object be the result of instructing the user agent’s Origin Policy Store to retrieve an Origin Policy object for origin.

  4. If object is null, abort these steps and return.

  5. Set policy to object’s body.

  6. For each member (member) in policy, execute the steps associated with the matching statement below:

    headers

    1. If member’s value is not valid as defined in §2.3.2 The headers member, abort these steps and return.

      Is there a better way to define validity? JSON schema?

      1. For each header in member’s value:

        1. Let name be header’s name, and value be header’s value.

        2. If header’s type is "fallback", and the result of parsing name in response’s header list is not null, skip the remaining substep, and proceed to the next header.

          Note: We check the header’s presence before applying baseline headers to response.

        3. Append name/value to response’s header list.

      Note: Once a policy is cached for an origin, the headers it applies will never change. User agents are encouraged to implement more performant algorithms making use of this invariant.

3.4.4. Does Origin Policy bypass request’s CORS-preflight request?

Given a request (request), this algorithm returns "Bypass" if the CORS-preflight request required for request can be fulfilled by examining the target origin’s Origin Policy Manifest, and "Does Not Bypass" if the preflight is required:

  1. Let manifest be the result of retrieving an Origin Policy for request’s url’s origin.

  2. If manifest is null, return "Does Not Bypass".

  3. Set manifest to manifest’s body.

  4. Let options be manifest’s cors-preflight member’s value if request’s credentials mode is "omit", and manifest’s unsafe-cors-preflight-with-credentials member’s value otherwise.

  5. Return "Does Not Bypass" if any of the following are true:

    1. options is not a CORSOptions object.

    2. request’s credentials mode is "include", and any of options’s origins, methods, or headers member’s values are "*".

    3. options’s origins is not "*", and the serialization of request’s origin is not an case-sensitive match for any of the items in options’s origins sequence.

    4. request’s method (method) is not a CORS-safelisted method, and options’s methods is not "*", and method is not a case-sensitive match for any of the items in options’s methods sequence.

    5. request’s header list (list) contains headers which are not CORS-safelisted request-headers, and options’s headers is not "*", and one or more of list’s non-CORS-safelisted request-headers' names is not an ASCII case-insensitive match for any of the items in options’s headers sequence.

  6. Return "Bypass".

4. Specification Considerations

4.1. Relation to Web App Manifests

It might make sense to combine this mechanism with the manifest notion defined in [APPMANIFEST]. There are a few distinctions, however, which lead to the current design:

  1. The Origin Policy Manifest is delivered synchronously during navigation, which gives it a number of valuable security properties. The web app manifest, on the other hand, is delivered inline via a link element, which makes it less valuable from that perspective.

  2. Web app manifests can live anywhere on an origin, and anywhere on any other origin, for that matter. This might make sense for the feature set it wishes to expose, but it would expose an origin to unnecessary risk to be quite so loose with origin policy manifest files.

Nevertheless, we should consider ways to merge this policy with [APPMANIFEST]. Perhaps we could accept a certain set of attributes was parsed iff certain properties of the request are true (same-origin, specific path, etc). Something to look into. For now, we’ve done the simple thing as a strawman.

4.2. Why .well-known?

This document forces the manifest to live as a resource accessible under a "well-known location" specific to Origin Policy manifests. It would increase flexibility of deployment and implementation if we allowed a developer to point to any location on an origin instead. Perhaps something like the following:

  Sec-Origin-Policy: /path/to/manifest.json

This would allow developers who control an application, but not the server on which the application runs, to define policy for themselves without bothering the sysadmins and filing internal tickets to get a .well-known directory created, populated, and mapped to something externally accessible.

So why does this document force a well-known location?

In short, this document posits that setting a policy for an origin is an action that should, in fact, involve the folks responsible for running the origin’s server, as it has cross-cutting effects upon every application hosted on an origin. Unlike a response-specific Content-Security-Policy header, the headers member can apply a baseline policy to all the responses an origin emits. This can be hugely benificial, but also hugely destructive.

Consider, for example, MegaCorp, Inc.'s https://example.com, which hosts a mail application, a mapping application, a document editing application, and so on. It would be unfortunate indeed if one of these applications accidentally pinned a policy to the entire origin which didn’t account for the rest of the origin’s contents. To mitigate that risk, this document forces the manifest to live in a shared location for an origin. The goal is explicitly to eilicit the kind of discussion and compromise within an origin that needs to happen in order to deploy an Origin Policy safely.

5. Authoring Considerations

5.1. Caching

Authors SHOULD set an appropriate Vary header in order to ensure that responses are cached correctly based on the relevant origin manifest.

Expand upon this.

5.2. Headers To Set

Authors SHOULD explicitly set a number of headers to construct a reasonable policy for their sites. Among the most important are:

Give some reasonable recommendations here, with links to tutorials if possible.

5.3. Performance

Servers SHOULD set a long cache lifetime for the manifest file, using the version to change policy for an origin as opposed to changing the existing policy and relying on cache expiration.

5.4. Server Push

To speed up the initial, synchronous request for the manifest file, servers SHOULD use the Server Push mechanism defined in [RFC7540] to get the manifest down to the client as quickly as possible.

  1. Send a PUSH_PROMISE frame with a minimal request for the manifest, aligning with the request generated in §3.4.2 Process response for Origin Policy..

  2. Begin delivering the response to the PUSH_PROMISE request.

Spell this out with an example.

5.5. CSP: Nonces and 'strict-dynamic'

Content Security Policy has introduced some dynamic mechanisms that are quite valuable in terms of encouraging deployment. Nonces, along with the 'strict-dynamic' expression are a good example of an implementation which requires a server to send a fresh value down with each response. Origin Policy can support such deployments via the "fallback" mechanism as follows:

MegaCorp, Inc. wishes to deploy a strong Content Security Policy which blocks plugins and framing altogether, but which uses 'strict-dynamic' along with a fresh nonce for each resource in order to reduce the risk of cross-site scripting. It can do so by combining a "baseline" and "fallback" policy in the Origin Policy Manifest, and delivering a Content-Security-Policy header along with each response.

That is, the manifest file might contain the following JSON:

{
  ...
  "headers": [
    {
      "name": "Content-Security-Policy",
      "value": "script-src 'none'",
      "type": "fallback"
    },
    {
      "name": "Content-Security-Policy",
      "value": "object-src 'none'; frame-ancestors 'self'",
      "type": "baseline"
    }
  },
  ...
}

And each resource’s response might contain the following header:

Content-Security-Policy: script-src 'nonce-{nonce-goes-here}' 'strict-dynamic'

The baseline policy will be applied to every response, blocking plugins and restricting framing regardless of the response’s other headers.

The fallback policy will be applied iff the response doesn’t itself provide a Content Security Policy, meaning that the resource’s policy will be applied, the nonce will be active, and script will execute as expected.

6. Privacy and Security Considerations

6.1. Tracking

We send an Sec-Origin-Policy header containing the currently cached manifest version up to the server along with every navigation request in order to reduce the performance impact of the synchronous request for the manifest. By informing the server which manifest the user has cached, the server can make intelligent decisions about when to initiate a server push, thereby avoiding the blocking behavior, and extra round-trip for a cancellation.

That said, the value of the Sec-Origin-Policy header (as well as the contents of the cached manifest), can be used to track users with only marginally less granularity and coverage than cookies allow. As such, the user agent MUST purge an origin’s cached manifests whenever a user instructs it to clear cookies or cached data for that origin. Additionally, if the user agent is configured to not attach cookies to a given request it MUST send 1 instead of the manifest version, advertising support for the feature, but not a particular cache entry.

6.2. The Sec- prefix

The Sec-Origin-Policy header Sec- prefix ensures that it is treated as a forbidden header name by Fetch: developers have no direct control over the header’s value, and cannot forge the header by injecting it via APIs like fetch() or XMLHttpRequest. It remains under the control of the user agent.

7. IANA Considerations

7.1. The Sec-Origin-Policy header

The permanent message header field registry should be updated with the following registration: [RFC3864]

Header field name

Sec-Origin-Policy

Applicable protocol

http

Status

WIP

Author/Change controller

W3C

Specification document

This specification (see §2.2 The Sec-Origin-Policy HTTP Header Field)

7.2. Creation of the well-known location origin-policy

This document defines a well-known location at which Origin Policy manifests may be found. In accordance with [RFC5758], the following registration should be made if and when the proposal in this document gains acceptance and implementation:

URI Suffix

origin-policy

Change Controller

W3C

Specification document

This document (see §2.3.1 The origin-policy well-known location)

Related information

The suffix origin-policy is expected to be followed by an additional path component which names a specific Origin Policy manifest (e.g. /.well-known/origin-policy/name). A request to /.well-known/origin-policy can be expected to redirect to the most current policy for an origin.

8. Acknowledgements

Mark Nottingham came up with more or less the same idea at more or less the same time (after waiting ~years for me to write this up) [SITE-WIDE-HEADERS]. I’ve swiped large swaths of his introduction, and turned a mess of a syntax into headers, as it’s better than what I was coming up with. Likewise, Takeshi Yoshino’s [ORIGIN-WIDE-CORS] proposal covers similar ground.

Mike O’Neill and Brad Hill helped mitigate the privacy implications of an earlier design for the feature-advertising request header. Anne van Kesteren helped ensure that the CORS-request preflight bits were sane.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSP3]
Mike West. Content Security Policy Level 3. URL: https://w3c.github.io/webappsec-csp/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[RFC3864]
G. Klyne; M. Nottingham; J. Mogul. Registration Procedures for Message Header Fields. September 2004. Best Current Practice. URL: https://tools.ietf.org/html/rfc3864
[RFC5234]
D. Crocker, Ed.; P. Overell. Augmented BNF for Syntax Specifications: ABNF. January 2008. Internet Standard. URL: https://tools.ietf.org/html/rfc5234
[RFC7231]
R. Fielding, Ed.; J. Reschke, Ed.. Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content. June 2014. Proposed Standard. URL: https://tools.ietf.org/html/rfc7231
[RFC7540]
M. Belshe; R. Peon; M. Thomson, Ed.. Hypertext Transfer Protocol Version 2 (HTTP/2). May 2015. Proposed Standard. URL: https://tools.ietf.org/html/rfc7540
[WebIDL]
Cameron McCormack; Boris Zbarsky; Tobie Langel. Web IDL. URL: https://heycam.github.io/webidl/
[WHATWG-URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/

Informative References

[APPMANIFEST]
Marcos Caceres; et al. Web App Manifest. URL: https://w3c.github.io/manifest/
[ORIGIN-WIDE-CORS]
Takeshi Yoshino. Origin-Wide CORS. URL: https://github.com/tyoshino/origin-wide-cors/blob/master/README.md
[REFERRER-POLICY]
Jochen Eisinger; Emily Stark. Referrer Policy. URL: https://w3c.github.io/webappsec-referrer-policy/
[RFC5758]
Q. Dang; et al. Internet X.509 Public Key Infrastructure: Additional Algorithms and Identifiers for DSA and ECDSA. January 2010. Proposed Standard. URL: https://tools.ietf.org/html/rfc5758
[RFC5785]
M. Nottingham; E. Hammer-Lahav. Defining Well-Known Uniform Resource Identifiers (URIs). April 2010. Proposed Standard. URL: https://tools.ietf.org/html/rfc5785
[RFC6797]
J. Hodges; C. Jackson; A. Barth. HTTP Strict Transport Security (HSTS). November 2012. Proposed Standard. URL: https://tools.ietf.org/html/rfc6797
[RFC7469]
C. Evans; C. Palmer; R. Sleevi. Public Key Pinning Extension for HTTP. April 2015. Proposed Standard. URL: https://tools.ietf.org/html/rfc7469
[RFC7541]
R. Peon; H. Ruellan. HPACK: Header Compression for HTTP/2. May 2015. Proposed Standard. URL: https://tools.ietf.org/html/rfc7541
[SITE-WIDE-HEADERS]
Mark Nottingham. Site-Wide HTTP Headers. URL: https://mnot.github.io/I-D/site-wide-headers/

IDL Index

//
// header member:
//
enum HTTPHeaderType { "baseline", "fallback" };

dictionary HTTPHeader {
  required USVString name;
  required USVString value;
  HTTPHeaderType type = "baseline";
};


//
// cors-preflight and unsafe-cors-preflight-with-credentials
//
enum Wildcard { "*" };
typedef (Wildcard or sequence<USVString>) CORSOptionList;

dictionary CORSOptions {
  CORSOptionList origins;
  CORSOptionList? methods;
  CORSOptionList? headers;
};

Issues Index

It may be a dangerous (or at least confusing) footgun to allow certain headers to be set on an origin (consider Content-Type or Content-Length, for instance). Perhaps it would be reasonable to limit the headers member to some whitelist of expected headers?
WebIDL is almost certainly the wrong way to describe the way the Sec-Origin-Policy header’s value is constructed. Still, it makes writing the spec quite a bit simpler, so. We’ll come up with something better later. Possibly as part of the JSON HTTP Header Field encoding work ongoing in the HTTP WG.
Write this algorithm.
Should we allow service workers to intercept/set policy? Maybe only if they are scoped to the whole origin?
Is there a better way to define validity? JSON schema?
Expand upon this.
Give some reasonable recommendations here, with links to tutorials if possible.
Spell this out with an example.