Content Security Policy Pinning

W3C Working Group Note,

This version:
http://www.w3.org/TR/2016/NOTE-csp-pinning-1-20160908/
Latest published version:
http://www.w3.org/TR/csp-pinning/
Editor's Draft:
https://w3c.github.io/webappsec-csp/pinning/
Version History:
https://github.com/w3c/webappsec-csp/commits/master/pinning/index.src.html
Feedback:
public-webappsec@w3.org with subject line “[csp-pinning] … message topic …” (archives)
Editor:
(Google Inc.)
Participate:
File an issue (open issues)
Obsoletion Notice

This specification is not being actively maintained, and should not be used as a guide for implementations. It may be revived in the future, but for now should be considered obsolete.

If you have questions or comments on this specification, please send an email to the editors.


Abstract

This Note provides a historical reference for a proposed mechanism to allow authors to instruct user agents to remember ("pin") and enforce a Content Security Policy for a set of hosts for a period of time.

Status of this document

Work on this document has been discontinued and it should not be referenced or used as a basis for implementation.

1. Introduction

This section is not normative.

Content Security Policy [CSP] defines a mechanism through which authors can manipulate the security properties of a given resource, providing the ability to mitigate the risk of a broad class of content-injection attacks. CSP, however, can only protect pages for which it is explicitly defined, which means that authors need to ensure that they’re delivering a reasonable policy for every page on their origin in order to have confidence that a particular set of restrictions will be consistently applied.

For example, it’s often the case that generic error-handling pages are constructed differently than "real" application pages. They’re easy to forget when auditing the security headers set for an origin, and can offer attackers a foot in the door if they contain injection vectors.

CSP Pinning attempts to address this concern by allowing authors to "pin" a baseline policy to an application’s host. Conceptually, this is quite similar to the approach taken by Strict Transport Security [RFC6797] and Public Key Pinning [PKP]: we define a new header, Content-Security-Policy-Pin which instructs a user agent to remember a baseline policy that will be enforced for any document and worker delivered by an application that doesn’t come with its own Content-Security-Policy header.

1.1. Use Cases

example.com has a number of applications running on the same origin; each has a specific set of resources it needs to load, so a single Content Security Policy would become unwieldy for the whole set of resources. Moreover, the admins aren’t exactly sure they have a clear understanding of all the applications running on subdomains; the marketing department went a bit wild with branded partnerships a year or two back.

After doing an audit of existing code, they have a good feel for the needs of individual applications, and give each a suitable policy. They decide to err on the side of caution, and pin a restrictive policy for pages they didn’t catch:

https://example.com/application1/ delivers the following HTTP response headers:
Content-Security-Policy-Pin: max-age: 10886400;
                             includeSubDomains;
                             default-src https:;
                             form-action 'none';
                             frame-ancestors 'none';
                             referrer no-referrer;
                             report-uri /csp-endpoint/pinned
Content-Security-Policy: script-src https://application1.cdn.com;
                         style-src https://application1.cdn.com;
                         connect-src 'self';
                         form-action 'self'

While https://example.com/application2/ delivers the following HTTP response headers:

Content-Security-Policy-Pin: max-age: 10886400;
                             includeSubDomains;
                             default-src https:;
                             form-action 'none';
                             frame-ancestors 'none';
                             referrer no-referrer;
                             report-uri /csp-endpoint/pinned
Content-Security-Policy: script-src https://application2.cdn.com;
                         style-src https://application2.cdn.com;

Meanwhile, they’ve forgotten about the coincidentally well-named https://forgotten-partnership.example.com/. It doesn’t send any CSP headers at all, and yet, it is still protected by the pinned policy for any users who have visited either Application 1 or Application 2.

2. Key Concepts and Terminology

2.1. Terms defined by this specification

pinned security policy
A security policy that is enforced for resources delivered from a protected host without their own policy. The pinned policy’s properties are defined in §3 Pinned Policy Delivery.
pinned policy cache
In order to persistently enforce policy for an origin, the user agent caches the following details about each pinned policy:
  1. The protected host: a hostname to which the policy applies (e.g. example.com)
  2. subdomains included: true if includeSubDomains is asserted, false otherwise.
  3. The policy expiration date: the moment at which a pinned policy is no longer applicable
  4. The policy directive set: a set of Content Security Policy directives [CSP] that the user agent MUST apply, according to its mode, for each Document and Worker served from protected host, (and, potentially, its subdomains) that does not provide its own policy.
  5. mode: monitor if the policy directive set is to be monitored, enforce if the policy directive set is to be enforced.

The Augmented Backus-Naur Form (ABNF) notation used in §3 Pinned Policy Delivery is specified in RFC5234. [ABNF]

3. Pinned Policy Delivery

A server MAY instruct a user agent to pin a single security policy by sending either a Content-Security-Policy-Pin or Content-Security-Policy-Report-Only-Pin HTTP response header field along with a resource. §4 Pinned Policy Processing defines the user agent’s behavior when it receives such a response.

Once a policy is pinned, it will be either enforced or monitored as specified for any resource that doesn’t enforce or monitor its own policy.

Note: Pinned policies are delivered only via HTTP header fields; no meta element delivery mechanism is defined. Moreover, pinned policies override policies delivered via meta elements. See §7.2 Pins override <meta> for authoring guidelines.

3.1. Content-Security-Policy-Pin Header Field

The Content-Security-Policy-Pin header field is the mechanism for delivering a pinned policy that the user agent MUST enforce for any resource which is not delivered with a Content-Security-Policy header (as described in the §4.1.3 Pin a policy to response algorithm).

The ABNF grammar is as follows:

"Content-Security-Policy-Pin:" 1#<policy-token production from CSP, Section 4.1>

Pinning a security policy is a somewhat dangerous operation, and requires some reasonable expectation that the pinning is in fact desired by a particular origin’s owner. To that end, a server MUST NOT send a Content-Security-Policy-Pin header with a resource delivered from an a priori insecure URL. The threat is discussed in more detail in §5.1 Hostile Pinning.

Note: This means that pinning is only practically available over HTTPS. This is intentional, as pinning is a powerful feature that ought to be limited to secure contexts [SECURE-CONTEXTS].

A server MUST NOT send more than one HTTP header field named Content-Security-Policy-Pin with a given resource representation.

A server SHOULD send a Content-Security-Policy-Pin with every resource representation in order to ensure that pinning takes place for a given user agent no matter how it accesses a site. The value of the header SHOULD be the same for every resource representation, as the goal is to enforce a consistent baseline policy for an entire set of hosts.

3.2. Content-Security-Policy-Report-Only-Pin Header Field

The Content-Security-Policy-Report-Only-Pin header field is the mechanism for delivering a pinned policy that the user agent MUST monitor for any resource which is not delivered with a Content-Security-Policy-Report-Only header (as described in the §4.1.3 Pin a policy to response algorithm).

The ABNF grammar is as follows:

"Content-Security-Policy-Report-Only-Pin:" 1#<policy-token production from CSP, Section 4.1>

As with Content-Security-Policy-Pin, a server MUST NOT send a Content-Security-Policy-Report-Only-Pin header with a resource delivered from an a priori insecure URL. The threat is discussed in more detail in §5.1 Hostile Pinning.

Note: This means that pin-reporting is only practically available over HTTPS. This is intentional, as pinning is a powerful feature that ought to be limited to secure contexts [SECURE-CONTEXTS].

A server MUST NOT send more than one HTTP header field named Content-Security-Policy-Report-Only-Pin with a given resource representation.

A server SHOULD send a Content-Security-Policy-Report-Only-Pin with every resource representation in order to ensure that pinning takes place for a given user agent no matter how they access a site. The value of the header SHOULD be the same for every resource representation, as the goal is to monitor a consistent baseline policy for an entire set of hosts.

What’s the impact of reporting? If headers can be injected into appspot.com or newyorktimes.com, can attackers use reporting to determine what apps you’re using, or what articles you’re reading? Brian has explored this space a bit. Perhaps dropping reporting from pinned policies would be reasonable. The main use-case I see would be discovering pieces of your site that you haven’t covered with a policy (e.g. where did the pin decrease attack surface?). It’s not clear we can even do that without the implications Brian suggests.

3.3. Pinned Policy Syntax

The grammar for a pinned policy is the same as the grammar for the Content-Security-Policy header, defined in Section 4.1 of the Content Security Policy specification.

A pinned policy’s value MUST contain a max-age directive, and MAY contain an includeSubDomains directive.

3.3.1. The max-age directive

The max-age directive specifies the number of seconds after the reception of the Content-Security-Policy-Pin HTTP response header field during which the UA SHOULD enforce the pinned policy.

The directive is defined via the following ABNF grammar:

directive-name  = "max-age"
directive-value = 1*DIGIT

The max-age directive MUST be present within the Content-Security-Policy-Pin header field. If it is not present, the header field will be ignored (see §4 Pinned Policy Processing for user agent requirements).

3.3.2. The includeSubDomains directive

The includeSubDomains directive signals to the user agent that the pinned policy defined in the Content-Security-Policy-Pin header field applies not only to the origin that served the resource representation, but also to any origin whose host component is a subdomain of the host component of the resource representation’s origin (see §4 Pinned Policy Processing for user agent requirements).

4. Pinned Policy Processing

The user agent discovers and processes pinned policies during fetching. Upon receiving a response, the user agent will:

  1. Sift through the HTTP headers according to the §4.1.1 Discover pinned policies for response algorithm to determine if the pinned policy cache for the response’s host needs to be updated.

  2. Update the pinned policy cache, according to the §4.1.2 Pin policy for origin in mode algorithm.

  3. Update the response’s headers to ensure that any relevant pinned policies are applied, according to the §4.1.3 Pin a policy to response algorithm.

We probably need a hook in [Fetch]. In particular, we need to ensure that we detect and pin a policy early enough for frame-ancestors and referrer to handle blocking and redirects.

Periodically, the user agent will run through the pinned policies it has stored in the pinned policy cache, and remove those that have expired, according to the §4.2.2 Remove expired pinned policies from the cache algorithm.

4.1. Fetching Algorithms

4.1.1. Discover pinned policies for response

Upon receiving a Response response containing at least one Content-Security-Policy-Pin header field, the user agent MUST peform the following steps:

  1. Let origin be the origin of response’s URL.
  2. Let value be the result of parsing Content-Security-Policy-Pin in response’s header list.
  3. If value is not null, then execute the §4.1.2 Pin policy for origin in mode algorithm, passing in value, the origin of response’s URL, and enforce.
  4. Let value be the result of parsing Content-Security-Policy-Report-Only-Pin in response’s header list.
  5. If value is not null, then execute the §4.1.2 Pin policy for origin in mode algorithm, passing in value, the origin of response’s URL, and monitor.

4.1.2. Pin policy for origin in mode

Given an Origin origin, a parsed set of directives policy, and a mode (either enforce or monitor), this algorithm defines the user agent behavior that results in a pinned policy for origin.

  1. If origin is an a priori insecure origin, output a developer-friendly warning, and abort these steps.
  2. Let host be the host component of origin.
  3. If host is an IPv4 or IPv6 address, output a developer-friendly warning, and abort these steps.
  4. Let policy be the result of executing the parse the policy algorithm on directives.
  5. If policy does not contain a max-age directive, then output a developer-friendly warning, and abort these steps.
  6. Let subdomains be true if an includeSubDomains is present in policy, and false otherwise.
  7. Let TTL be the number of seconds specified in policy’s max-age directive. If more than one such directive is present, let TTL be the largest value specified.
  8. Let expiration be the current time, plus TTL.
  9. Remove any max-age and includeSubDomains directives from policy.
  10. Let pinned be the result of executing §4.2.1 Get the mode pinned policy for host for mode and host.
  11. If pinned is not null, then update the pinned policy pinned as follows:
    1. If max-age is 0, then remove pinned from the pinned policy cache and abort these steps.
    2. Otherwise:
      1. Set pinned’s policy expiration date to expiration.
      2. Set pinned’s subdomains included to subdomains.
      3. Set pinned’s policy directive set to policy.
  12. Otherwise, host is not a protected host. If TTL is not 0, then:
    1. Let pinned be a new pinned policy.
    2. Set pinned’s protected host to host.
    3. Set pinned’s policy expiration date to expiration.
    4. Set pinned’s subdomains included to subdomains.
    5. Set pinned’s policy directive set to policy.
    6. Set pinned’s mode to mode.
    7. Add pinned to the pinned policy cache.

4.1.3. Pin a policy to response

Upon receiving a Response response, ensure that it contains appropriate Content-Security-Policy headers by performing the following steps:

  1. Let host be the host component of response’s URL’s origin.
  2. Let pinned be the result of executing §4.2.1 Get the mode pinned policy for host for enforce and host.
  3. If pinned is not null:
    1. Let value be the result of parsing Content-Security-Policy in response’s header list.
    2. If value is null:
      1. Append a header named Content-Security-Policy with a value of pinned’s policy directive set to response’s header list.
  4. Let pinned be the result of executing §4.2.1 Get the mode pinned policy for host for monitor and host.
  5. If pinned is not null:
    1. Let value be the result of parsing Content-Security-Policy-Report-Only in response’s header list.
    2. If value is null:
      1. Append a header named Content-Security-Policy-Report-Only with a value of pinned’s policy directive set to response’s header list.

4.2. Pinned Policy Cache Algorithms

4.2.1. Get the mode pinned policy for host

Given a host, and a mode mode, this algorithm walks through the pinned policy cache, and returns the first matching policy. If no policies match, this algorithm returns null.

Note: There ought to be at most one policy that matches, given the constraints in §4.1.2 Pin policy for origin in mode.

  1. For each policy in the pinned policy cache:
    1. If policy’s mode is not mode, skip to the next policy in the pinned policy cache.
    2. Let match type be the result of applying the Known HSTS Host domain name matching algorithm specified in [RFC6797] to host and policy’s protected host.
    3. If match type is Superdomain Match, and policy’s subdomains included is true, then return policy.
    4. If match type is Congruent Match, then return policy.
  2. Return null.

4.2.2. Remove expired pinned policies from the cache

Periodically, the user agent MUST remove expired policies from the pinned policy cache. Removal will have no web-visible effect, as expired policies will not modify Responses during fetching, but expired policies can have privacy impact if they aren’t removed completely (as they offer evidence that a particular user visited a particular host at some point in the past).

Expired entries can be removed via the following steps:

  1. For each policy in the list of pinned policies contained in the pinned policy cache:
    1. If policy’s policy expiration date is prior to the current time, remove policy from the pinned policy cache.

5. Security Considerations

5.1. Hostile Pinning

An active network attacker who is able to inject headers into a site’s responses may attempt to maliciously pin a security policy for a host and its subdomains. Pinning default-src 'none' on a page that wasn’t built to work under such restrictions could deny service for an entire application.

Unlike public key pinning [PKP], however, pinning a security policy cannot completely deny access to a site. This means that maliciously (or accidentally) pinned policies can be easily overridden in two ways:

  1. Authors SHOULD send a valid security policy down with each HTTP response, and use the pin only as a backup (see §7.1 Pins as a default).

    Note: A future version of this specification may add a directive which prevents overriding the pinned policy (no-override?). This would allow authors to choose a stricter deployment model, but would remove this override possibility.

  2. Authors may also rescind a pinned policy by sending a new Content-Security-Policy-Pin header with a max-age of 0.

Moreover, the risk of malicious injection is mitigated by the fact that we only accept pins over secure and authenticated connections.

6. Privacy Considerations

6.1. Fingerprinting

Similar to HSTS and HPKP, a pinned security policy could be used as a "supercookie", setting a distinct policy for each user which can be used as an identifier in combination with (or instead of) HTTP cookies.

For example, the report-uri directive could contain a unique identifier (report-uri https://example.com/endpoint?id=123) which could identify a user based on correlating violation reports with user activity.

To mitigate this risk, user agents MUST:

  1. Clear the pinned policy cache when the user clears her browsing data (cookies, site data, history, etc).
  2. Refuse to process Set-Cookie response headers during the send violation reports algorithm.

Can we assume that subdomains are really owned by the owner of the root domain?

7. Authoring Considerations

7.1. Pins as a default

Explain something about the theory; pins act as a baseline for resources that don’t otherwise have a policy. Explain layering, granularity, etc.

7.2. Pins override <meta>

Pinned policies are applied before meta elements can be discovered. This means that a resource delivered without a header that specified a security policy will be subject to the policy pinned for its host, even if it then delivers a policy via the mechanisms described in the HTML <meta> element section of [CSP].

8. IANA Considerations

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

8.1. Content-Security-Policy-Pin

Header field name
Content-Security-Policy-Pin
Applicable protocol
http
Status
standard
Author/Change controller
W3C
Specification document
This specification (See Content-Security-Policy-Pin Header Field)

8.2. Content-Security-Policy-Report-Only-Pin

Header field name
Content-Security-Policy-Report-Only-Pin
Applicable protocol
http
Status
standard
Author/Change controller
W3C
Specification document
This specification (See Content-Security-Policy-Report-Only-Pin Header Field)

9. Acknowledgements

Yan Zhu kicked my butt to get this document out the door. I stole concepts wholesale from both HSTS and PKP.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[ABNF]
D. Crocker, Ed.; P. Overell. Augmented BNF for Syntax Specifications: ABNF. January 2008. Internet Standard. URL: https://tools.ietf.org/html/rfc5234
[CSP]
Mike West; Dan Veditz. Content Security Policy. WD. URL: https://w3c.github.io/webappsec-csp/
[Fetch]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML5]
Ian Hickson; et al. HTML5. 28 October 2014. REC. URL: https://www.w3.org/TR/html5/
[MIXED-CONTENT]
Mike West. Mixed Content. 8 October 2015. CR. URL: https://www.w3.org/TR/mixed-content/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[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
[RFC6454]
A. Barth. The Web Origin Concept. December 2011. Proposed Standard. URL: https://tools.ietf.org/html/rfc6454
[RFC6797]
J. Hodges; C. Jackson; A. Barth. HTTP Strict Transport Security (HSTS). November 2012. Proposed Standard. URL: https://tools.ietf.org/html/rfc6797
[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
[WHATWG-DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[WHATWG-URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WORKERS]
Ian Hickson. Web Workers. 24 September 2015. WD. URL: https://www.w3.org/TR/workers/

Informative References

[PKP]
Chris Evans; Chris Palmer; Ryan Sleevi. Public Key Pinning Extension for HTTP. Draft. URL: https://tools.ietf.org/html/draft-ietf-websec-key-pinning
[SECURE-CONTEXTS]
Mike West; Yan Zhu. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/

Issues Index

What’s the impact of reporting? If headers can be injected into appspot.com or newyorktimes.com, can attackers use reporting to determine what apps you’re using, or what articles you’re reading? Brian has explored this space a bit. Perhaps dropping reporting from pinned policies would be reasonable. The main use-case I see would be discovering pieces of your site that you haven’t covered with a policy (e.g. where did the pin decrease attack surface?). It’s not clear we can even do that without the implications Brian suggests.
We probably need a hook in [Fetch]. In particular, we need to ensure that we detect and pin a policy early enough for frame-ancestors and referrer to handle blocking and redirects.
Can we assume that subdomains are really owned by the owner of the root domain?
Explain something about the theory; pins act as a baseline for resources that don’t otherwise have a policy. Explain layering, granularity, etc.