Strawman Draft: https://mikewest.github.io/origin-policy/
TL;DR: Developers can jam all 23k of their Content-Security-Policy
into a manifest file at /.well-known/origin-policy/[name-goes-here]
, along with a number of other headers and configuration options, and instruct user agents to apply the metadata contained within to every response served from the origin.
Overview
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. These headers explicitly alters the state of an entire Moreover, many headers are deployed in such a way as to be practically 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:
-
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 -
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 occurrence: 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.
Origin Policy 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.
Example
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 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
...
Origin-Policy: 0
...
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
...
Origin-Policy: "policy-1"
...
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": {
"fallback": [
{
"name": "Content-Security-Policy",
"value": "script-src 'self' https://cdn.example.com"
},
{
"name": "Referrer-Policy",
"value": "origin-when-cross-origin"
}
],
"baseline": [
{
"name": "Content-Security-Policy",
"value": "object-src 'none'; frame-ancestors 'none'"
},
{
"name": "Strict-Transport-Security",
"value": "max-age=10886400; includeSubDomains; preload"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
}
]
},
"cors-preflight": { /* TODO(mkwst): Syntax? */ },
}
Subsequent requests from the same client will contain the version of the policy currently cached for the origin. In this case:
GET / HTTP/1.1
Host: example.com
Connection: keep-alive
...
Origin-Policy: "policy-1"
...