This specification defines a mechanism that allows developers to selectively enable and disable use of various browser features and APIs.

This is a work in progress and may change without any notices.

Introduction

The web-platform provides an ever-expanding set of features and APIs, offering richer functionality, better developer ergonomics, and improved performance. However, a missing piece is the ability for the developer to selectively enable, disable, or modify the behavior of some of these browser features and APIs within their application:

  1. The developer may want to selectively _disable_ access to certain browser features and APIs to "lock down" their application, as a security or performance precaution, to prevent own and third-party content executing within their application from introducing unwanted or unexpected behaviors within their application.
  2. The developer may want to selectively _enable_ access to certain browser features and APIs which may be disabled by default - e.g. some features may be disabled by default in embedded context unless explicitly enabled; some features may be subject to other policy requirements.
  3. The developer may want to use the policy to assert a promise to a client or an embedder about the use—or lack of thereof—of certain features and APIs. For example, to enable certain types of "fast path" optimizations in the browser, or to assert a promise about conformance with some requirements set by other embedders - e.g. various social networks, search engines, and so on.

This specification defines a feature policy mechanism that addresses the above use cases.

Examples

SecureCorp Inc. wants to disable use of WebRTC and Geolocation APIs within their application. It can do so by delivering the following HTTP response header to define a feature policy:

Feature-Policy: {"disable":["webrtc","geolocation"]}

Unless specified otherwise, the default target is "`\*`", which means that the specified features will be disabled for all browsing contexts regardless of their origin.

SecureCorp Inc. wants to disable use of Geolocation API within all browsing contexts whose origin is "`https://example.com`". It can do so by delivering the following HTTP response header to define a feature policy:

Feature-Policy: {"disable":["geolocation"], "target":["https://example.com"]}

The target is a list of one or more origins, which can include the applications origin and any third-party origin.

SecureCorp Inc. is hosting an application on "`https://example.com`" and wants to disable WebRTC on its origin but enable it for a whitelisted embedee ("`https://other.com`"). It can do so by delivering the following HTTP response header to define a feature policy:

Feature-Policy: {"disable":["webrtc"], "target":["https://example.com"]},
        {"enable":["webrtc"], "target":["https://other.com"]}

Some features are disabled by default in embedded contexts. The enable policy allows the application to selectively enable such features for whitelisted origins.

FastCorp Inc. wants to disable use of synchronous `script` elements, synchronous `XMLHttpRequest`'s and use of `document.write` within their application. It can do so by delivering the following HTTP response header to define a feature policy:

Feature-Policy: {"disable":["sync-xhr","sync-script","docwrite"]}

Other and related mechanisms

[[HTML5]] defines a [sandbox attribute] for `iframe` elements that allows developers to reduce the risk of including potentially untrusted content by imposing restrictions on content's abilities - e.g. prevent it from submitting forms, running scripts and plugins, and more. The [sandbox directive] defined by [[CSP2]] extends this capability to any resource, framed or not, to ask for the same set of restrictions - e.g. via an HTTP response header (`Content-Security-Policy: sandbox`). These mechanisms enable the developer to:

However, there are several limitations to the above mechanism: the developer cannot automatically apply a policy across all contexts, which makes it hard or impossible to enforce consistently in some cases (e.g. due to third-party content injecting frames, which the developer does not control); there is no mechanism to selectively enable features that may be off by default; the sandbox mechanism uses a whitelist approach which is impossible to extend without compatibility risk.

Feature Policy is intended to be used in combination with the sandbox mechanism (i.e. it does not duplicate feature controls already covered by sandbox), and provides an extensible mechanism that addresses the above limitations.

Framework

Policies

A feature policy may be applied to a [Window] or [WorkerGlobalScope] and consists of:

Directives

Each directive is a tuple consisting of:

Delivery

`Feature-Policy` HTTP Header Field

The Feature-Policy HTTP [response] header field is used to deliver a feature policy from a server to client. The header's value is represented by the following ABNF [[!RFC5234]]:

          Disable = 1#json-field-value
                    ; See Section 2 of [[HTTP-JFV]], and Section 2 of [[RFC7159]]
        

The policy is parsed as defined in , and the header’s value is interpreted as an array of JSON objects, as described in Section 4 of [[!HTTP-JFV]].

The policy MUST be delivered via an HTTP response header field to be processed by the client; the client MUST ignore any policy set via `meta` element's `http-equiv` attribute.

The feature policy needs to be set before the response is processed and before any subresource requests are initiated. Once active the policy also needs to apply for the lifetime of the document and is not allowed to be modified at runtime. Due to these requirements, `meta` element cannot be used to set the policy.

The following sections define the set of known members in each JSON object the header’s value defines. Future versions of this document may define additional such members, and user agents MUST ignore unknown members when parsing the header.

The target member

The OPTIONAL target member defines the scope in which the directive is enforced. The member's name is "`target`" and the recognized are either an array of serialized origins, or the string "`\*`". If an unknown value is specified, or if no member named "`target`" is present in the object, the directive's target will be set to "`\*`".

The disable member

The OPTIONAL disable member defines the disable policy for the directive. The member's name is "`disable`" and the value is a list of ASCII strings.

The enable member

The OPTIONAL enable member defines the enable policy for the directive. The member's name is "`enable`" and the value is a list of ASCII strings.

Process response policy

Given a [response] (response) and a [request] (request), this algorithm returns a feature policy.

  1. Abort these steps if any of the following conditions are true:
    1. response’s [HTTPS state] is not "modern", and the [origin] of response’s [url] is not [potentially trustworthy].
    2. response’s [header list] does not contain a [header] whose [name] is "`Feature-Policy`".
  2. Let header be the [value] of the [header] in response’s [header list] whose name is "`Feature-Policy`".
  3. Let feature policy be the result of executing on header.

Parse response policy from value

Given a string (value) this algorithm will return a feature policy.

  1. Let list be the result of executing the algorithm defined in Section 4 of [[!HTTP-JFV]]. If that algorithm results in an error, abort these steps.
  2. Let policy be an empty list.
  3. For each item in list:
    1. Let target be an empty list.
    2. If item has a target member whose value is an array, then for each origin in the array:
      1. Let result be the result of executing the [URL parser] on origin
      2. If result is not `failure`, then append the [origin of result][origin-of-url] to target.
    3. If target is still an empty list, set target to "`\*`".
    4. Let disable be the result of executing on the item's disable member's value.
    5. Let enable be the result of executing on the item's enable member's value.
    6. Let tuple be a new directive tuple whose values are set as follows:
      "`target`"
      Set to target.
      "`disable`"
      Set to disable.
      "`enable`"
      Set to enable.
    7. Append tuple to policy.
  4. Return policy.

Disable Policy

The disable policy allows a developer to turn off certain features for a Document or Worker.

Processing

Parse disable features

Given a list, this algorithm returns a list of valid disable features, which may be empty.

  1. Let valid-features be an empty list.
  2. If list is null or empty, return valid-features.
  3. For each item in list:
    1. Convert item to ASCII-lowercase.
    2. If item's string value is not one of the valid disable features, ignore item, and continue to the next item.
    3. Append item to valid-features.
  4. Return valid-features.

Features

This section defines the list of valid disable features and their effect when applied via a directive as part of a feature policy.

`cookie`

Disables `document.cookie`: when present, the attribute's getter and setter will throw a SecurityError.

Given the following header:

  Feature-Policy: {"disable":["cookie"]}

The following JavaScript code will throw a `SecurityError` exception:

  document.cookie = "a=b;Secure;SameSite";
  alert(document.cookie);

`domain`

Disables `document.domain`: when present, the attribute's getter and setter will throw a SecurityError.

Given the following header:

  Feature-Policy: {"disable":["domain"]}

The following JavaScript code will throw a `SecurityError` exception:

  document.domain = "example.com";
  alert(document.domain);

`docwrite`

Disables `document.write`: when called, throws "`NotSupportedError`" `DOMException`.

Given the following header:

  Feature-Policy: {"disable":["docwrite"]}

The following JavaScript code will throw a `NotSupportedError` exception:

  document.write("...");

`geolocation`

Disables Geolocation API. [[!GEOLOCATION-API]]

`midi`

Disables Web MIDI API. [[!WEBMIDI]]

`notifications`

Disables Notification API. [[!NOTIFICATIONS]]

`push`

Disables Push API. [[!PUSH-API]]

`sync-script`

Disables synchronous `script` elements. When this policy is set, such scripts are ignored by the user agent.

Given the following header:

  Feature-Policy: {"disable":["sync-script"]}

The following HTML will not result in execution of `script1.js`, as it has neither a [`defer`] nor an [`async`] attribute. The remaining scripts will execute normally because they have their [non-blocking] flag set.

  <script src="/script1.js"></script>
  <script src="/script2.js" async></script>
  <script src="/script3.js" defer></script>
  <script>
    const scriptEl = document.createElement("script");
    scriptEl.src = "/script4.js";
    document.body.appendChild(scriptEl);
  </script>

`sync-xhr`

Disables synchronous `XMLHttpRequest` API: when [open() method](xhr-open) is called with async argument set to true, an `InvalidAccessError` except will be thrown.

Given the following header:

  Feature-Policy: {"disable":["sync-xhr"]}

The following JavaScript code will throw a `InvalidAccessError` exception:

  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/foo", true);

`webrtc`

Disables WebRTC. [[!WEBRTC]]

Integrations

This document defines a set of algorithms which other specifications will use in order to implement the restrictions which Feature Policy defines. The integrations are outlined here for clarity, but those external documents are the normative references which ought to be consulted for detailed information.

Integration with HTML

  1. Document and WorkerGlobalScope objects have a Feature Policy List, which is either the empty set or a feature policy. This property is the empty set unless otherwise specified, and is populated via the algorithm.
  2. The algorithm is called during the "Initialising a new Document object" and "Run a Worker" algorithms.
  3. `document.cookie`'s getter and setter algorithms call into the algorithm to determine whether or not to throw, as follows:

    On getting:

    1. If the document is a cookie-averse Document object, return the empty string.
    2. If the document's origin is an opaque origin, throw a "`SecurityError`" `DOMException`.
    3. If the algorithm returns "`Disabled`" when executed upon "`cookie`" and the document's global object, throw a "`SecurityError`" `DOMException`.
    4. Otherwise the user agent must return the cookie-string for the document's URL for a "non-HTTP" API, decoded using UTF-8 decode without BOM.

    On setting:

    1. If the document is a cookie-averse Document object, then the user agent must skip the remaining substeps.
    2. If the document's origin is an opaque origin, throw a "`SecurityError`" `DOMException`.
    3. If the algorithm returns "`Disabled`" when executed upon "`cookie`" and the document's global object, throw a "`SecurityError`" `DOMException`.
    4. Otherwise, the user agent must act as it would when receiving a set-cookie-string for the document's URL via a "non-HTTP" API, consisting of the new value encoded as UTF-8.
  4. `document.domain`'s getter and setter algorithms call into the algorithm to determine whether or not to throw, as follows:

    1. Add the following step after the current step 1 of the attribute's getter:
      1. If the algorithm returns "`Disabled`" when executed upon "`domain`" and the document's global object, throw a "`SecurityError`" `DOMException`.
    2. Add the following step after the current step 1 of the attribute's setter:
      1. If the algorithm returns "`Disabled`" when executed upon "`domain`" and the document's global object, throw a "`SecurityError`" `DOMException`.
  5. [`document.write` method](docwrite-method) calls into the algorithm to determine whether or not to throw, as follows:

    1. Add the following step after the current step 1:
      1. If the algorithm returns "`Disabled`" when executed upon "`docwrite`" and the document's global object, throw a "`NotSupportedError`" `DOMException`.
  6. The "[prepare a script]" algorithm calls into algorithm to determine whether or not to execute a script, as follows:

    1. Add the following step before the current step 10 (which is the first of several "disabled" checks):
      1. If the `script` element's "`non-blocking`" flag is unset, the `script`'s type is "`classic`", and the algorithm returns "`Disabled`" when executed upon the `script` element's node document's global object, then abort these steps at this point. The script is not executed.
Monkey-patching! As soon as we know that this is the direction we wish to persuse, upstream all of this.

Initialize global's Feature Policy from response

Given a [response] (response) and a global object (global), this algorithm populates global's Feature Policy List

  1. If global is a `Window` and its `Document` has a parent browsing context or opener browsing context (context):
    1. Let document be the active document in context.
    2. For each directive in document's Feature Policy List:
      1. Add directive to global's `Document`'s Feature Policy List if any of the following are true:
        1. directive's target is "`\*`"
        2. directive's target is an array containing global's origin
        3. response's url's scheme is a local scheme
  2. If global is a `WorkerGlobalScope`:
    1. For each document in global's documents:
      1. For each directive in document's Feature Policy List:
        1. Add directive to global's Feature Policy List if either of the following are true:
          1. directive's target is "`\*`"
          2. directive's target is an array containing global's origin
          3. response's url's scheme is a local scheme
  3. Let policy be the result of executing on response.
  4. For each directive in policy, add directive to global's Feature Policy List.

Is feature disabled for global?

Given a string (feature) and a global object (global), this algorithm returns "`Disabled`" if feature should be considered disabled, and "`Enabled`" otherwise.

  1. Let policy be global's Feature Policy List
  2. If policy is empty, return "`Enabled`".
  3. For each directive in policy:
    1. If directive's disable contains feature:
      1. Return "`Disabled`".
  4. Return "`Enabled`".

Integration with XMLHttpRequest

The `open() method` calls into the algorithm to determine whether or not to throw, as follows:

  1. Add the following step after the current step 3:
    1. If async is false and the algorithm returns "`Disabled`" when executed upon "`sync-xhr`" and the document's global object, throw "`InvalidAccessError`" exception.

Enable Policy

The enable policy allows a developer to turn on certain features for a Document or Worker.

Processing

Parse enable features

Given a list, this algorithm returns a list of valid enable features, which may be empty.

  1. Let valid-features be an empty list.
  2. If list is null or empty, return valid-features.
  3. For each item in list:
    1. Convert item to ASCII-lowercase.
    2. TODO... should we merge/refactor this with disable parse?
    3. Append item to valid-features.
  4. Return valid-features.

IANA Considerations

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

Header field name
Feature-Policy
Applicable protocol
http
Status
standard
Author/Change controller
W3C
Specification document
Feature Policy API

Privacy and Security

TODO

[sandbox attribute]: https://html.spec.whatwg.org/multipage/the-iframe-element.html#attr-iframe-sandbox [sandbox directive]: https://www.w3.org/TR/2014/WD-CSP11-20140211/#sandbox [window]: https://www.w3.org/TR/html5/browsers.html#dom-window [workerglobalscope]: https://html.spec.whatwg.org/multipage/workers.html#workerglobalscope [response]: https://fetch.spec.whatwg.org/#concept-response [request]: https://fetch.spec.whatwg.org/#concept-request [HTTPS state]: https://fetch.spec.whatwg.org/#concept-response-https-state [origin]: https://url.spec.whatwg.org/#concept-url-origin [url]: https://fetch.spec.whatwg.org/#concept-response-url [potentially trustworthy]: https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy [header list]: https://fetch.spec.whatwg.org/#concept-response-header-list [header]: https://fetch.spec.whatwg.org/#concept-header [name]: https://fetch.spec.whatwg.org/#concept-header-name [value]: https://fetch.spec.whatwg.org/#concept-header-value [xhr-open]: https://xhr.spec.whatwg.org/#the-open()-method [docwrite method]: https://html.spec.whatwg.org/#dom-document-write [async]: https://html.spec.whatwg.org/#attr-script-async [defer]: https://html.spec.whatwg.org/#attr-script-defer [prepare a script]: https://html.spec.whatwg.org/#prepare-a-script [non-blocking]: https://html.spec.whatwg.org/#non-blocking [header-append]: https://fetch.spec.whatwg.org/#concept-header-list-append [header-list]: https://fetch.spec.whatwg.org/#concept-request-header-list [fetching algorithm]: https://fetch.spec.whatwg.org/#fetching [global object]: https://html.spec.whatwg.org/multipage/webappapis.html#global-object [current global object]: https://html.spec.whatwg.org/#current-global-object [client]: https://fetch.spec.whatwg.org/#concept-request-client [sender requirements]: https://greenbytes.de/tech/webdav/draft-reschke-http-jfv-02.html#rfc.section.3 [non-blocking]: https://html.spec.whatwg.org/#non-blocking [URL parser]: https://url.spec.whatwg.org/#concept-url-parser [origin-of-url]: https://url.spec.whatwg.org/#concept-url-origin