功能政策简介

Eric Bidelman

摘要

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

功能政策本身是开发者与浏览器之间的小型选择启用协议,有助于实现我们构建(和维护)优质 Web 应用的目标。

简介

构建 Web 应用是一场艰难的冒险。构建出色的 Web 应用并实现出色的性能并采用所有最新的最佳实践并非易事。随着时间的推移,保持这种良好体验更加困难。随着项目的不断发展,开发者会加入进来,新功能会陆续推出,代码库也在不断壮大。您曾经获得的棒极体验 TM 可能会开始恶化,用户体验也开始受到影响!功能政策旨在帮助您了解相关情况。

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

下面列举了一些示例来说明您可以使用“功能政策”实现哪些功能:

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

政策是开发者与浏览器之间的一种约定。它们会告知浏览器开发者的意图,从而在应用试图偏离正轨并做出一些不良行为时帮助我们保持诚实。如果网站或嵌入的第三方内容试图违反开发者预先选择的任何规则,浏览器会采用更好的用户体验替换相应行为,或完全阻止该 API。

使用功能政策

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

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

若要使用特性政策,最简单的方法是发送 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,则浏览器会将其阻止。即使用户之前已授予分享其位置信息的权限,也是如此。

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

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

Feature-Policy: geolocation 'self'

iframe allow 属性

使用特性政策的第二种方法是控制 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
  • 禁止将 geolocation 用于除网页自己的源站和 https://example.com 之外的所有浏览上下文。
  • 允许 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 代码库。文中会对部分政策进行一些解释说明。

FAQ

何时使用功能政策?

所有政策都是用户自选的,因此请在必要时使用功能政策。例如,如果您的应用是一个图库,maximum-downscaling-image 政策将帮助您避免向移动设备视口发送巨大的图片。

在使用 document-writesync-xhr 等其他政策时,应格外小心。开启它们可能会破坏广告等第三方内容。另一方面,功能政策可以作为一项直觉检查,确保您的网页绝不使用这些糟糕的 API!

我是否在开发或生产环境中使用功能政策?

两者都有。我们建议在开发期间启用政策,并在生产环境中启用政策。在开发过程中启用政策可以帮助您在正确的轨道开始。这有助于您及早发现任何意外的回归问题。在生产环境中启用政策,保证为用户提供特定用户体验。

有办法向我的服务器举报违反政策的行为吗?

Reporting API 正在开发中!与网站选择接收关于 CSP 违规情况弃用的报告类似,您也能实际收到关于功能政策违规行为的报告。

iframe 内容的继承规则是什么?

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

iframe会沿用其父级页面的政策。如果 iframe 具有 allow 属性,则父页面和 allow 列表之间更严格的政策将生效。如需详细了解 iframe 的用法,请参阅 iframe 上的 allow 属性

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

哪些浏览器支持功能政策?

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

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

总结

特性政策有助于提供良好的用户体验和性能。这在开发或维护应用时特别有用,因为它有助于避免潜在漏洞侵入您的代码库。

其他资源