互联网工程任务组 (IETF) I. Grigorik
备忘录请求: 8942 Y. Weiss
类别: 试验性 Google
ISSN: 2070-1721 2021年2月

HTTP 客户端提示


摘要

HTTP 定义了主动内容协商,以允许服务器基于请求首部表达的用户代理特性为给定请求选择合适的响应。实际上,用户代理通常不愿发送那些请求首部,因为不清楚它们是否会被使用,并且发送这些首部会影响性能和隐私。

本文档定义了一个 Accept-CH 响应头,服务器可以使用它来通告其使用请求首部进行主动内容协商,并提供了一组用于创建此类首部的指南,俗称“客户端提示”。

本文状态

本文件不是互联网标准轨道规范;发布用于审查、试验性实现和评估。

本文件为互联网社区定义了一种试验性协议。它是互联网工程任务组 (IETF) 的产物,代表 IETF 社区的共识。它已接受公开审查并经互联网工程指导小组 (IESG) 批准发布。并非所有被 IESG 批准的文档都适合成为任何级别的互联网标准;参见 RFC 7841 第2节

关于本文件当前状态、任何勘误或如何提供反馈的信息,请访问 https://www.rfc-editor.org/info/rfc8942

Copyright Notice

Copyright (c) 2021 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.


1. 介绍

有成千上万种不同的设备访问网络,每种设备具有不同的设备能力和偏好信息。这些设备能力包括硬件和软件特性,以及动态的用户和用户代理偏好。历史上,希望服务器基于这些能力来优化内容交付和用户体验的应用程序不得不依赖被动识别(例如,通过将 User-Agent 首部字段(第5.5.3节,见 [RFC7231])与已建立的用户代理签名数据库匹配)、使用 HTTP Cookie [RFC6265] 和 URL 参数,或使用这些及类似机制的某种组合来实现临时的内容协商。

这些技术的设置和维护成本高,并且在应用和服务器之间不可移植。它们也使得用户代理和服务器都难以理解在协商过程中哪些数据是必需和正在使用的:

  • 用户代理检测无法可靠地识别所有静态变量,无法推断动态的用户代理偏好,需要外部设备数据库,不利于缓存,并依赖被动指纹识别面。
  • 基于 Cookie 的方法在应用和服务器之间不可移植,通过要求 JavaScript 执行增加了客户端延迟,并且不利于缓存。
  • URL 参数类似于基于 Cookie 的方法,缺乏可移植性且由于需要在每个资源的 URL 中编码内容协商数据而难以部署。

主动内容协商(RFC7231 第3.4.1节)提供了一种替代方法;用户代理使用指定的、定义良好的请求首部来通告其能力和特性,以便服务器可以基于这些请求首部(或其他隐含的特性)选择(或构造)适当的响应。

然而,传统的主动内容协商技术通常意味着用户代理大量发送这些请求首部。这会引发性能问题(因为请求中会产生“膨胀”),也会带来隐私问题;被动地提供此类信息允许服务器悄然对用户进行指纹识别。

本文件定义了客户端提示(Client Hints)框架,使服务器能够选择性地启用特定的主动内容协商功能并据此调整其内容,同时提供使用该框架的内容协商机制的指南。本文档还定义了一个新的响应头 Accept-CH,允许源服务器明确请求用户代理在后续请求中发送这些首部。

客户端提示通过确保用户代理仅在实际会被使用时才发送请求首部,从而缓解了性能问题;并通过要求服务器通过 Accept-CH 响应头显式选择并披露所需首部,将被动指纹识别向主动方式转变,从而缓解了被动指纹识别的隐私问题。

本文档不定义客户端提示的具体使用方式。此类使用需要在各自的规范中定义。

此类使用的一个示例是用户代理客户端提示 [UA-CH]

1.1. 符号约定

在本文档中,关键词 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", 和 "OPTIONAL" 应按 BCP 14 中描述的方式解释(见 [RFC2119][RFC8174]),仅当它们以全大写形式出现时,才具有该含义。

本文档使用扩展巴科斯-诺尔范式 (ABNF) 表示法,参见 [RFC5234]


2. 客户端提示请求头字段

客户端提示请求头字段是用户代理用于指示可被服务器用于选择合适响应的数据的 HTTP 首部字段。每个字段传达用户代理偏好,服务器可以使用这些偏好来调整和优化响应。

2.1. 发送客户端提示

用户代理基于其默认设置、用户配置以及服务器在 Accept-CH 中表达的偏好来选择在请求中发送哪些客户端提示。用户代理和服务器可以使用下面概述的选择加入(opt-in)机制来协商需要发送的首部字段,以实现高效的内容适配,并且它们可以可选地使用额外机制(例如在 [CLIENT-HINTS-INFRASTRUCTURE] 中概述的机制)来协商委托策略,从而控制第三方对相同首部字段的访问。用户代理 SHOULD 要求对任何不被视为低熵的提示进行选择加入。有关暴露低熵量的提示示例,请参阅 [CLIENT-HINTS-INFRASTRUCTURE] 中的低熵提示表。

实现者在实现对客户端提示的支持时需要注意指纹识别的影响,并遵循本文档“安全性考量”一节中概述的注意事项(见 第4节)。

2.2. 服务器对客户端提示的处理

当收到包含一个或多个客户端提示首部字段的请求时,服务器可以基于其中的信息优化响应。当这样做时,如果资源是可缓存的,服务器 MUST 还必须生成一个 Vary 响应头字段(参见 RFC7231 第7.1.4节),以指示哪些提示可能影响所选择的响应以及该响应是否适用于后续请求。

服务器 MUST 忽略它们不理解或不支持的提示。当前没有机制让服务器向用户代理指示哪些提示被忽略。

此外,服务器可以生成与所使用的提示指定的相关值的附加响应头字段,以帮助客户端处理。


3. 声明服务器支持

服务器可以使用下面描述的机制来通告对客户端提示的支持。

3.1. Accept-CH 响应头字段

Accept-CH 响应头字段指示服务器对其值中所示提示的支持。希望通过客户端提示接收用户代理信息的服务器 SHOULD 尽早在响应中添加 Accept-CH 响应头。

Accept-CH 是一种结构化首部(Structured Header)(参见 [RFC8941])。其值 MUST 是一个 sf-list(参见 RFC8941 第3.1节)且其成员为 Token(参见 RFC8941 第3.3.4节)。其 ABNF 如下:

  Accept-CH = sf-list

例如:

Accept-CH: Sec-CH-Example, Sec-CH-Example-2

当用户代理接收到包含 Accept-CH 的 HTTP 响应时,表示该源已选择接收所指示的请求首部字段以用于随后同源请求。如果该选择加入是通过非安全传输(使用与 HTTPS 不同的方案)发送的,则 MUST 忽略该选择加入。该选择加入 SHOULD 被持久化并绑定到该源,以便在用户会话期间(由用户代理定义)向服务器的源发送客户端提示。一个选择加入会覆盖先前持久化的选择加入值,并且 SHOULD 用其替代先前的持久化值。

基于上面的 Accept-CH 示例,若在用户代理导航到 "https://site.example" 时收到该响应并通过安全传输发送,则持久化的 Accept-CH 偏好将绑定到 "https://site.example"。随后它会用于诸如 "https://site.example/foobar.html" 的导航,但不会用于例如 "https://foobar.site.example/"。它也会用于由该导航响应构造的页面发起的任何同源资源请求(例如 "https://site.example/image.jpg"),但不会用于跨源资源请求(例如 "https://thirdparty.example/resource.js")。该偏好不会扩展到从其他源发起到 "https://site.example" 的资源请求(例如从 "https://other.example/" 的导航)。

3.2. 与缓存的交互

当基于一个或多个客户端提示选择响应且资源是可缓存的时,服务器需要生成一个 Vary 响应头字段(参见 [RFC7234])以指示哪些提示可能影响所选择的响应以及该响应是否适用于后续请求。

Vary: Sec-CH-Example

上述示例指示缓存键需要包含 Sec-CH-Example 首部字段。

Vary: Sec-CH-Example, Sec-CH-Example-2

上述示例指示缓存键需要包含 Sec-CH-Example 和 Sec-CH-Example-2 首部字段。


4. 安全性考量

4.1. 信息暴露

在依赖本文档的功能中使用的请求首部字段会暴露用户环境的信息,以实现隐私保护的主动内容协商并避免暴露被动指纹识别向量。然而,实现者需要记住,在最坏情况下,不受控制和不受监测的主动指纹识别并不优于被动指纹识别。为了提供用户隐私的好处,用户代理需要应用进一步的策略以防止滥用通过客户端提示暴露的信息。

这些功能暴露的信息可能会揭示关于用户的新信息,实现者应考虑下列注意事项、建议和最佳实践。

潜在的基本假设是,通过请求首部暴露关于用户的信息在安全角度上等价于通过其他方式暴露这些信息。(例如,如果请求的源可以使用 JavaScript API 访问该信息并将其传输到其服务器。)

由于客户端提示是一种显式的选择加入机制,这意味着希望访问用户环境信息的服务器需要主动请求该信息,从而使客户端和隐私研究人员能够跟踪哪些源收集这些数据,并可能据此采取行动。基于首部的选择加入意味着可以消除被动指纹识别向量。例如,用户代理可以减少在 User-Agent 字符串中暴露的信息,同时通过用户代理客户端提示(User-Agent Client Hints)以主动方式提供该信息。或者,用户代理可以以不增加被动指纹识别面的方式暴露通过脚本已可获得的信息(例如 Save-Data 客户端提示 <https://wicg.github.io/savedata/#save-data-request-header-field>)。支持客户端提示功能并向选择加入的服务器发送某些信息的用户代理 SHOULD 避免以被动方式发送等效信息。

因此,依赖本文档定义客户端提示首部的功能 MUST NOT 提供用户代理未向应用程序公开的新信息,例如现有的请求首部、HTML、CSS 或 JavaScript 所提供的信息。

此类功能需要考虑所暴露信息的以下方面:

熵:
暴露高度细粒度的数据可能被用来帮助识别跨不同源的用户。减少可表达的首部字段值集合,或将其限制为一个枚举范围,其中通告的值接近但并非当前值的精确表示,可以通过确保多个用户发送相同值来提高隐私并降低可连通性风险。
敏感性:
该功能 SHOULD NOT 暴露用户敏感信息。为此,向应用可用但需特定用户操作(例如权限提示或用户激活)才能获得的信息 SHOULD NOT 作为客户端提示暴露。
随时间变化:
除非状态变化本身也被暴露(例如通过 JavaScript 回调),否则该功能 SHOULD NOT 暴露随时间变化的用户信息。

不同的功能将在低熵、非敏感和静态信息(例如用户代理信息)与高熵、敏感和动态信息(例如地理位置)之间的不同位置被定位。用户代理需要权衡特定功能提供的价值与这些考虑,并可能希望对不同功能或其他细粒度基础有不同的策略。

实现者应该考虑用户和服务器可控的机制与策略,以控制通告哪些客户端提示首部字段:

  • 实现者 SHOULD 将某些或全部客户端提示首部字段的传递限制为仅限选择加入的源,除非选择加入的源已明确将请求客户端提示首部字段的权限委托给另一个源。
  • 考虑提供允许用户在隐私顾虑与带宽限制之间权衡的用户选择机制的实现者,还需要考虑向用户解释所涉隐私含义(例如被动指纹识别的风险)可能具有挑战性甚至不切实际。
  • 针对某些用例或威胁模型的实现 MAY 避免传输某些或全部客户端提示首部字段。例如,避免传输可能带来更高可连通性风险的首部字段。

用户代理 MUST 在站点数据、浏览缓存、Cookie 或类似数据被清除时清除持久化的选择加入偏好。

4.2. 部署与安全风险

部署新的请求首部需要考虑若干事项:

  • 可能与已有首部字段名的冲突
  • 首部字段值中所传达数据的属性

新的客户端提示作者应谨慎考虑它们是否需要由客户端内容(例如脚本)添加,或是否需要由用户代理专门设置。在后一种情况下,首部字段名上的 Sec- 前缀具有防止脚本和其他应用内容在用户代理中设置它们的效果。使用 "Sec-" 前缀向服务器表明该值是由用户代理生成的——而不是由应用内容生成的。有关详细信息,请参见 [FETCH]

按惯例,作为客户端提示的请求首部建议使用 CH- 前缀,以便更容易将它们识别为使用该框架的首部;例如 CH-Foo 或带有 "Sec-" 前缀时为 Sec-CH-Foo。这样可以使它们更容易通过编程方式识别(例如,隐私过滤器通过程序化方式从请求中剥离不被识别的提示)。

通过 Accept-CH 选择加入机制协商的客户端提示请求首部 MUST 具有与 sf-token 相匹配的字段名(参见 RFC8941 第3.3.4节)。

4.3. 滥用检测

跟踪对主动指纹识别信息访问的用户代理 SHOULD 考虑将客户端提示首部的发出视为对等的等同于对等 API 的访问。

关于客户端提示滥用的研究可能会考察包含客户端提示的请求与具有不同值或不带值的请求之间的 HTTP 响应差异。这可能被用来揭示哪些客户端提示正在被使用,从而允许研究人员进一步分析该使用情况。


5. 发送提示的成本

向服务器发送客户端提示会增加请求字节数。部分增加可以通过 HTTP 头压缩方案减轻,但每增加一个提示仍会导致带宽使用的上升。服务器 SHOULD 在选择加入以接收客户端提示时考虑这一点,并且 SHOULD NOT 选择加入接收提示,除非这些提示用于内容适配目的。

由于请求字节数增加,依赖本文档定义客户端提示的功能 MAY 考虑将这些提示的发送限制在某些请求目标(参见 [FETCH]),在这些目标上提示更可能有用。


6. IANA 注意事项

依赖本文档的功能预计会在“永久消息首部字段名”注册表中注册新增的请求首部字段(参见 [RFC3864])。

本文档定义了 "Accept-CH" HTTP 响应头字段;IANA 已在相同注册表中注册了该字段。

6.1. Accept-CH

首部字段名:
Accept-CH
适用协议:
HTTP
状态:
试验性
作者/变更控制:
IETF
规范文档:
本 RFC 第3.1节
相关信息:
用于客户端提示

7. 参考文献


致谢

感谢 Mark Nottingham、Julian Reschke、Chris Bentzel、Ben Greenstein、Tarun Bansal、Roy Fielding、Vasiliy Faronov、Ted Hardie、Jonas Sicking、Martin Thomson 以及众多其他 IETF HTTP 工作组成员提供的宝贵帮助和反馈。


作者地址

Ilya Grigorik
Google
EMail: ilya@igvita.com
URI: https://www.igvita.com/
Yoav Weiss
Google
EMail: yoav@yoav.ws
URI: https://blog.yoav.ws/