利用用户代理客户端提示改善用户隐私并提升开发者体验

User-Agent Client Hints 是 Client Hints API 的一项新扩展,可让开发者以注重隐私保护且符合人体工学的方式访问用户浏览器的相关信息。

借助客户端提示,开发者可以主动请求有关用户设备或条件的信息,而无需从用户代理 (UA) 字符串中解析这些信息。提供此替代路线是最终缩小用户代理字符串粒度的首要步骤。

了解如何更新依赖于解析 User-Agent 字符串的现有功能,以便改用 User-Agent 客户端提示。

背景

当网络浏览器发出请求时,会包含有关浏览器及其环境的信息,以便服务器能够启用分析功能并自定义响应。此字符串早在 1996 年就已定义(适用于 HTTP/1.0 的 RFC 1945),您可以在其中找到用户代理字符串的原始定义,其中包含示例:

User-Agent: CERN-LineMode/2.15 libwww/2.17b3

此标头旨在按重要性顺序指定产品(例如浏览器或库)和注释(例如版本)。

User-Agent 字符串的状态

在过去的几十年里,此字符串积累了大量与发出请求的客户端相关的其他详细信息(以及由于向后兼容性而产生的垃圾内容)。查看 Chrome 的当前 User-Agent 字符串时,我们可以看到:

Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4076.0 Mobile Safari/537.36

上述字符串包含有关用户的操作系统和版本、设备型号、浏览器品牌和完整版本的信息,足以推断出它是一款移动浏览器,更不用说出于历史原因而对其他浏览器的大量引用。

这些参数与可能值的多样性相结合,意味着 User-Agent 字符串可能包含足够的信息,能够唯一地识别个别用户。

User-Agent 字符串支持许多合法的使用情形,对开发者和网站所有者而言具有重要作用。不过,保护用户隐私免受隐秘跟踪方法的侵害也至关重要,而默认发送 UA 信息与此目标背道而驰。

此外,还需要改进 User-Agent 字符串的 Web 兼容性。它是非结构化的,因此解析它会导致不必要的复杂性,这通常是导致 bug 和网站兼容性问题的原因,会给用户带来不良体验。这些问题对不太常用的浏览器的用户影响更大,因为网站可能未针对其配置进行测试。

全新推出用户代理客户端提示

用户代理客户端提示可让您以更注重隐私保护的方式访问相同的信息,从而让浏览器最终减少用户代理字符串默认广播所有信息的情况。客户端提示会强制执行一种模型,其中服务器必须向浏览器请求一组有关客户端的数据(提示),而浏览器会应用自己的政策或用户配置来确定要返回的数据。这意味着,现在访问权限将以明确且可审核的方式进行管理,而不是默认公开所有“用户代理”信息。开发者还可以受益于更简单的 API,无需再使用正则表达式!

当前的一组客户端提示主要描述浏览器的显示和连接功能。您可以在使用客户端提示自动选择资源中详细了解该过程,但下面简要介绍了该过程。

服务器通过标头请求特定的客户端提示:

⬇️ 服务器响应

Accept-CH: Viewport-Width, Width

或使用元标记:

<meta http-equiv="Accept-CH" content="Viewport-Width, Width" />

然后,浏览器可以选择在后续请求中发回以下标头:

⬆️ 后续请求

Viewport-Width: 460
Width: 230

服务器可以选择提供不同的响应,例如以适当的分辨率提供图片。

用户代理客户端提示会使用可通过 Accept-CH 服务器响应标头指定的 Sec-CH-UA 前缀来扩展属性范围。如需了解所有详情,请先参阅说明文档,然后深入了解完整提案

Chromium 89 中的 User-Agent 客户端提示

自 Chrome 89 起,用户代理客户端提示已默认启用。

默认情况下,浏览器会返回浏览器品牌、重要 / 主要版本、平台以及客户端是否为移动设备的指示器:

⬆️ 所有要求

Sec-CH-UA: "Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "macOS"

User-Agent 响应和请求标头

⬇️ 响应 Accept-CH
⬆️ 请求标头
⬆️ 请求
示例值
说明
Sec-CH-UA "Chromium";v="84",
"Google Chrome";v="84"
浏览器品牌及其重要版本的列表。
Sec-CH-UA-Mobile ?1 布尔值,指示浏览器是否在移动设备上(?1 表示 true,?0 表示 false)。
Sec-CH-UA-Full-Version "84.0.4143.2" [已废弃]浏览器的完整版本。
Sec-CH-UA-Full-Version-List "Chromium";v="84.0.4143.2",
"Google Chrome";v="84.0.4143.2"
浏览器品牌及其完整版本的列表。
Sec-CH-UA-Platform "Android" 设备的平台,通常是操作系统 (OS)。
Sec-CH-UA-Platform-Version "10" 平台或操作系统的版本。
Sec-CH-UA-Arch "arm" 设备的底层架构。虽然这可能与显示网页无关,但网站可能希望提供默认采用正确格式的下载内容。
Sec-CH-UA-Model "Pixel 3" 设备型号。
Sec-CH-UA-Bitness "64" 底层架构的位数(即整数或内存地址的位数)

交换示例

一个示例交换如下所示:

⬆️ 来自浏览器的初始请求
浏览器正在向网站请求 /downloads 页面,并发送其默认的基本 User-Agent。

GET /downloads HTTP/1.1
Host: example.site

Sec-CH-UA: "Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"
Sec-CH-UA-Mobile: ?1
Sec-CH-UA-Platform: "Android"

⬇️ 服务器响应
服务器会发回相应网页,并额外请求完整的浏览器版本和平台。

HTTP/1.1 200 OK
Accept-CH: Sec-CH-UA-Full-Version-List

⬆️ 后续请求
浏览器向服务器授予对额外信息的访问权限,并在所有后续请求中发回额外提示。

GET /downloads/app1 HTTP/1.1
Host: example.site

Sec-CH-UA: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
Sec-CH-UA-Mobile: ?1
Sec-CH-UA-Full-Version-List: " Not A;Brand";v="99.0.0.0", "Chromium";v="98.0.4738.0", "Google Chrome";v="98.0.4738.0"
Sec-CH-UA-Platform: "Android"

JavaScript API

除了标头之外,您还可以通过 navigator.userAgentData 在 JavaScript 中访问 User-Agent。您可以分别通过 brandsmobile 属性访问默认的 Sec-CH-UASec-CH-UA-MobileSec-CH-UA-Platform 标头信息:

// Log the brand data
console.log(navigator.userAgentData.brands);

// output
[
  {
    brand: 'Chromium',
    version: '93',
  },
  {
    brand: 'Google Chrome',
    version: '93',
  },
  {
    brand: ' Not;A Brand',
    version: '99',
  },
];

// Log the mobile indicator
console.log(navigator.userAgentData.mobile);

// output
false;

// Log the platform value
console.log(navigator.userAgentData.platform);

// output
"macOS";

您可以通过 getHighEntropyValues() 调用访问其他值。“高熵”一词是指信息熵,也就是说,这些值揭示的有关用户浏览器的信息量。与请求其他标头一样,返回哪些值(如果有)取决于浏览器。

// Log the full user-agent data
navigator
  .userAgentData.getHighEntropyValues(
    ["architecture", "model", "bitness", "platformVersion",
     "fullVersionList"])
  .then(ua => { console.log(ua) });

// output
{
   "architecture":"x86",
   "bitness":"64",
   "brands":[
      {
         "brand":" Not A;Brand",
         "version":"99"
      },
      {
         "brand":"Chromium",
         "version":"98"
      },
      {
         "brand":"Google Chrome",
         "version":"98"
      }
   ],
   "fullVersionList":[
      {
         "brand":" Not A;Brand",
         "version":"99.0.0.0"
      },
      {
         "brand":"Chromium",
         "version":"98.0.4738.0"
      },
      {
         "brand":"Google Chrome",
         "version":"98.0.4738.0"
      }
   ],
   "mobile":false,
   "model":"",
   "platformVersion":"12.0.1"
}

演示

您可以在自己的设备上访问 user-agent-client-hints.glitch.me,试用标头和 JavaScript API。

提示生命周期和重置

系统会在浏览器会话期间或在指定另一组提示之前,发送通过 Accept-CH 标头指定的提示。

也就是说,如果服务器发送:

⬇️ 响应

Accept-CH: Sec-CH-UA-Full-Version-List

然后,浏览器将在对该网站的所有请求中发送 Sec-CH-UA-Full-Version-List 标头,直到浏览器关闭为止。

⬆️ 后续请求

Sec-CH-UA-Full-Version-List: " Not A;Brand";v="99.0.0.0", "Chromium";v="98.0.4738.0", "Google Chrome";v="98.0.4738.0"

不过,如果收到另一个 Accept-CH 标头,则该标头会完全替换浏览器当前发送的提示。

⬇️ 响应

Accept-CH: Sec-CH-UA-Bitness

⬆️ 后续请求

Sec-CH-UA-Platform: "64"

之前请求的 Sec-CH-UA-Full-Version-List 不会发送

最好将 Accept-CH 标头视为指定了为该网页所需的完整提示集,这意味着浏览器随后会为该网页上的所有子资源发送指定的提示。虽然提示会保留到下次导航,但网站不应依赖或假定它们会被传送。

您还可以使用此方法在响应中发送空白 Accept-CH,以有效清除浏览器发送的所有提示。请考虑在用户重置偏好设置或退出您网站的任何位置添加此代码。

此模式也与通过 <meta http-equiv="Accept-CH" …> 标记实现提示的方式相匹配。系统只会在网页发起的请求中发送请求的提示,而不会在任何后续导航中发送。

提示作用域和跨源请求

默认情况下,客户端提示仅会在同源请求中发送。也就是说,如果您在 https://example.com 上请求特定提示,但要优化的资源位于 https://downloads.example.com 上,则这些资源不会收到任何提示。

如需允许跨源请求中的提示,必须使用 Permissions-Policy 标头指定每个提示和来源。如需将其应用于 User-Agent Client Hints,您需要将该提示转换为小写形式并移除 sec- 前缀。例如:

⬇️ example.com 的回复

Accept-CH: Sec-CH-UA-Platform-Version, DPR
Permissions-Policy: ch-ua-platform-version=(self "downloads.example.com"),
                    ch-dpr=(self "cdn.provider" "img.example.com");

⬆️ 发给downloads.example.com的请求

Sec-CH-UA-Platform-Version: "10"

⬆️ cdn.providerimg.example.com 的请求

DPR: 2

在哪里使用用户代理客户端提示?

简要回答是,您应重构解析 User-Agent 标头或使用访问相同信息的任何 JavaScript 调用(即 navigator.userAgentnavigator.appVersionnavigator.platform)的所有实例,以改用 User-Agent 客户端提示。

更进一步,您应重新审视自己对 User-Agent 信息的使用,并尽可能将其替换为其他方法。通常,您可以通过利用渐进增强、功能检测或自适应设计来实现相同的目标。依赖 User-Agent 数据的基本问题在于,您始终需要在要检查的媒体资源与其启用的行为之间维护映射。这是一种维护开销,旨在确保您的检测全面且保持最新状态。

请注意这些注意事项,User-Agent 客户端提示代码库列出了一些适用于网站的有效用例

用户代理字符串会怎样?

我们的计划是通过减少现有用户代理字符串公开的身份信息量,尽可能降低在网络上进行隐秘跟踪的可能性,同时不会对现有网站造成过多干扰。现在,我们推出了 User-Agent 客户端提示,让您有机会在对 User-Agent 字符串进行任何更改之前,了解和试用新功能。

最终,我们将减少“User-Agent”字符串中的信息,使其保持旧版格式,同时仅提供与默认提示相同的高级浏览器和重要版本信息。在 Chromium 中,此更改已推迟到至少 2022 年,以便生态系统有更多时间评估新的用户代理客户端提示功能。

您可以通过在 Chrome 93 中启用 about://flags/#reduce-user-agent 标志来测试此功能的某个版本(注意:此标志在 Chrome 84 到 92 版中名为 about://flags/#freeze-user-agent)。出于兼容性原因,这将返回包含历史条目的字符串,但其中包含经过排错的具体信息。例如:

Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36

缩略图:Unsplash 上的 Sergey Zolkin