功能政策简介

摘要

功能政策允许 Web 开发者在浏览器中有选择地启用、停用和修改某些 API 和 Web 功能的行为。它与 CSP 类似,但控制的是功能,而不是控制安全性!

功能政策本身是开发者和浏览器之间很少的选择接受协议,可以帮助实现我们构建(和维护)高质量 Web 应用的目标。

简介

构建网站是一场精彩的冒险。构建一款出色的 Web 应用不仅性能优异,而且采用了所有最新的最佳实践,实在是太难了。随着时间的推移,保持良好的体验更加困难。随着项目的发展,开发者会参与进来,推出新功能,并且代码库也会在不断增长。您获得那种优质体验后,体验可能会开始恶化,用户体验开始下降!功能政策旨在确保您始终能够正常使用。

通过 Feature Policy,您可以选择一组“政策”,以便浏览器对整个网站使用的特定功能强制执行。这些政策会限制网站可以访问哪些 API,或者针对某些功能修改浏览器的默认行为。

以下是您可以使用 Feature Policy 执行的操作示例:

  • 更改 autoplay 在移动设备和第三方视频上的默认行为
  • 限制网站使用敏感 API,例如 cameramicrophone
  • 允许 iframe 使用 fullscreen API。
  • 禁止使用过时的 API,例如同步 XHR 和 document.write()
  • 确保图片大小合适(例如,防止布局抖动),并且对于视口来说不会过大(例如浪费用户的带宽)。

政策是开发者与浏览器之间的约定。它们可以告知浏览器开发者的意图,从而帮助我们在应用试图出格并做不好的事情时让我们坦诚相见。如果网站或嵌入的第三方内容试图违反开发者预先选择的任何规则,浏览器会替换该行为以提供更出色的用户体验,或者完全阻止该 API。

使用功能政策

功能政策提供两种控制功能:

  1. 通过 Feature-Policy HTTP 标头。
  2. 使用 iframe 上的 allow 属性。

使用 Feature Policy 的最简单方法是随网页响应发送 Feature-Policy HTTP 标头。此标头的值为您希望浏览器在给定源站遵循的一项或一组政策:

Feature-Policy: <feature> <allow list origin(s)>

来源许可名单可以采用以下几种不同的值:

  • *:顶级浏览上下文和嵌套浏览上下文 (iframe) 中允许使用该功能。
  • 'self':顶级浏览上下文和同源嵌套浏览上下文中允许使用此功能。在嵌套浏览上下文中的跨源文档中,我们不允许使用这种方式。
  • 'none':在顶级浏览上下文中禁止使用该功能,在嵌套浏览上下文中则禁止使用。
  • <origin(s)>:要启用政策的特定源站(例如 https://example.com)。

示例

假设您想阻止所有内容在整个网站中使用 Geolocation API。为此,您可以为 geolocation 功能发送 'none' 的严格许可名单:

Feature-Policy: geolocation 'none'

如果有一段代码或 iframe 尝试使用 Geolocation API,浏览器会将其屏蔽。即使用户之前已授权分享其位置信息,也是如此。

违反设置的地理位置政策
违反设置的地理位置政策。

在其他情况下,不妨稍微放宽此政策。我们可以通过在许可名单中设置 'self',允许我们自己的出发地使用 Geolocation API,但禁止第三方内容访问它:

Feature-Policy: geolocation 'self'

iframe allow 属性

使用 Feature Policy 的第二种方法是控制 iframe 中的内容。使用 allow 属性为嵌入式内容指定政策列表:

<!-- Allow all browsing contexts within this iframe to use fullscreen. -->
<iframe src="https://example.com..." allow="fullscreen"></iframe>

<!-- Equivalent to: -->
<iframe src="https://example.com..." allow="fullscreen *"></iframe>

<!-- Allow only iframe content on a particular origin to access the user's location. -->
<iframe
  src="https://another-example.com/demos/..."
  allow="geolocation https://another-example.com"
></iframe>

现有的 iframe 属性会怎么样?

由功能政策控制的部分功能具有可控制其行为的现有属性。例如,<iframe allowfullscreen> 是允许 iframe 内容使用 HTMLElement.requestFullscreen() API 的属性。此外,还有 allowpaymentrequestallowusermedia 属性,分别用于允许 Payment Request APIgetUserMedia()

尽可能尝试使用 allow 属性,而不是这些旧属性。如果您需要支持向后兼容性,可以将 allow 属性与等效的旧版属性一起使用(例如 <iframe allowfullscreen allow="fullscreen">)。需要注意的是,限制性更强的政策将胜出。例如,以下 iframe 不允许进入全屏模式,因为 allow="fullscreen 'none'"allowfullscreen 的限制更严格:

<!-- Blocks fullscreen access if the browser supports feature policy. -->
<iframe allowfullscreen allow="fullscreen 'none'" src="..."></iframe>

同时控制多项政策

通过发送 HTTP 标头(其中包含以 ; 分隔的政策指令列表),可以同时控制多项功能:

Feature-Policy: unsized-media 'none'; geolocation 'self' https://example.com; camera *;

或为每个政策发送单独的标头:

Feature-Policy: unsized-media 'none'
Feature-Policy: geolocation 'self' https://example.com
Feature-Policy: camera *;

该示例将执行以下操作:

  • 禁止所有浏览上下文使用 unsized-media
  • 除了网页自己的来源和 https://example.com 之外,禁止将 geolocation 用于所有浏览上下文。
  • 允许所有浏览上下文使用“camera”。

示例 - 在 iframe 上设置多项政策

<!-- Blocks the iframe from using the camera and microphone
     (if the browser supports feature policy). -->
<iframe allow="camera 'none'; microphone 'none'"></iframe>

JavaScript API

虽然 Chrome 60 在 iframe 中添加了对 Feature-Policy HTTP 标头和 allow 属性的支持,但 JavaScript API 是在 Chrome 74 中添加的。

此 API 允许客户端代码确定网页、框架或浏览器允许使用哪些功能。您可以在 document.featurePolicy(对于主文档)或 frame.featurePolicy(对于 iframe)下使用其优势。

示例

以下示例说明了在 https://example.com 网站上发送 Feature-Policy: geolocation 'self' 政策的结果:

/* @return {Array<string>} List of feature policies allowed by the page. */
document.featurePolicy.allowedFeatures();
// → ["geolocation", "midi",  "camera", "usb", "autoplay",...]

/* @return {boolean} True if the page allows the 'geolocation' feature. */
document.featurePolicy.allowsFeature('geolocation');
// → true

/* @return {boolean} True if the provided origin allows the 'geolocation' feature. */
document.featurePolicy.allowsFeature(
  'geolocation',
  'https://another-example.com/'
);
// → false

/* @return {Array<string>} List of feature policies allowed by the browser
regardless of whether they are in force. */
document.featurePolicy.features();
// → ["geolocation", "midi",  "camera", "usb", "autoplay",...]

/* @return {Array<string>} List of origins (used throughout the page) that are
   allowed to use the 'geolocation' feature. */
document.featurePolicy.getAllowlistForFeature('geolocation');
// → ["https://example.com"]

政策列表

那么可以通过功能策略控制哪些功能呢?

目前,缺乏关于已实现哪些政策以及如何使用这些政策的文档。随着不同的浏览器采用该规范并实施各种政策,该列表也会随着时间推移而不断增加。功能政策并非一成不变,因此肯定需要实用的参考文档。

目前,有两种方法可以查看哪些功能是可控制的。

  • 请观看我们的演示版 Feature Policy Kitchen Sink。其中提供了在 Blink 中实施的每项政策的示例。
  • 如需查看功能名称列表,请查看 Chrome 的源代码
  • 针对 about:blank 查询 document.featurePolicy.allowedFeatures() 以查找列表:
        ["geolocation",
         "midi",
         "camera",
         "usb",
         "magnetometer",
         "fullscreen",
         "animations",
         "payment",
         "picture-in-picture",
         "accelerometer",
         "vr",
        ...
  • 访问 chromestatus.com,了解 Blink 中已实施或正在考虑的政策。

如需确定如何使用其中某些政策,请查看规范的 GitHub 代码库其中对部分政策进行了解释说明。

常见问题解答

何时使用功能政策?

所有政策都是可选择启用的政策,因此请适时或适时地使用功能政策。例如,如果您的应用是图库,则 maximum-downscaling-image 政策可帮助您避免将巨幅图片发送到移动设备视口。

在使用 document-writesync-xhr 等其他政策时应格外小心。开启这类设置可能会中断广告等第三方内容。另一方面,功能政策可以进行直接检查,以确保您的网页绝不会使用这些糟糕的 API!

我应该在开发或生产环境中使用功能政策吗?

两者都有。我们建议在开发期间启用政策,并使政策在生产环境中保持有效。在开发过程中启用政策有助于您迈出正确的第一步。这有助于您及时发现任何意外回归问题。在生产环境中将政策保持为启用状态,以确保为用户提供特定的用户体验。

有办法向我的服务器报告违反政策的情况吗?

Reporting API 正在开发中! 与网站如何选择接收关于 CSP 违规弃用的报告类似,您也能收到关于普遍违反功能政策的报告。

iframe 内容的继承规则有哪些?

脚本(第一方或第三方)会沿用其浏览上下文的政策。这意味着顶级脚本会继承主文档的政策。

iframe 会继承其父页面的政策。如果 iframe 具有 allow 属性,则以父页面和 allow 列表之间更严格的政策为准。如需详细了解 iframe 的用法,请参阅 iframe 上的 allow 属性

不会。政策的有效期针对的是单页导航响应。如果用户导航到新页面,则必须在新响应中明确发送 Feature-Policy 标头,才能应用政策。

哪些浏览器支持 Feature Policy?

如需详细了解浏览器支持,请访问 caniuse.com

目前,Chrome 是唯一支持功能政策的浏览器。不过,由于整个 API Surface 都可选择启用或检测功能,因此功能政策非常适合用于渐进式增强

总结

特征政策有助于为实现更出色的用户体验和出色性能提供良好的途径。在开发或维护应用时,这个功能特别方便,因为它有助于避免潜在“枪手”潜入您的代码库之前。

其他资源