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

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

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

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

背景

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

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

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

用户代理字符串的状态

在过去的几十年里,此字符串积累了大量与发出请求的客户端相关的其他详细信息(以及由于向后兼容性而产生的垃圾内容)。查看 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 字符串支持许多合法用例,对开发者和网站所有者而言具有重要作用。不过,保护用户隐私免受隐秘跟踪方法的侵害同样至关重要,而默认发送 UA 信息与此目标背道而驰。

在用户代理字符串方面,还需要提高 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”字符串中的信息,使其保持旧版格式,同时仅提供与默认提示相同的高级浏览器和重要版本信息。在 Chromium 中,此项变更已至少推迟到 2022 年,以便为生态系统提供更多时间来评估新的用户代理客户端提示功能。

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

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

Sergey Zolkin 发布的 Unsplash 网站缩略图