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

用户代理客户端提示是对 Client Hints API 的新扩展,可让开发者以可保护隐私且符合人体工程学的方式访问用户浏览器的相关信息。

借助客户端提示,开发者可以主动请求有关用户的设备或状况的信息,而无需从用户代理 (UA) 字符串中将其解析出来。最终降低用户代理字符串粒度的第一步是提供此备用路由。

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

背景

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

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

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

用户代理字符串的状态

在中间的几十年间,此字符串累积了发出请求的客户端的各种其他详细信息(以及因向后兼容性而进行的保留)。在查看 Chrome 的当前用户代理字符串时,我们可以看到:

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

上面的字符串包含有关用户的操作系统和版本、设备型号、浏览器品牌和完整版本的信息,并提供足够的线索来推断它是一款移动浏览器;由于历史原因,我们更不用说对其他浏览器的引用了。

这些参数与具有多种多样的可能值的组合意味着用户代理字符串可以包含足够的信息,可以对各个用户进行唯一标识。

用户代理字符串支持许多合法的用例,对于开发者和网站所有者来说也非常重要。不过,保护用户隐私免受隐蔽跟踪方法的侵害也至关重要,而默认发送 UA 信息会违反这一目标。

在用户代理字符串方面,还需要提高 Web 兼容性。它是非结构化的,因此对其进行解析会导致不必要的复杂性,这通常是造成错误和网站兼容性问题对用户造成伤害的原因。这些问题还会对不常见浏览器的用户造成严重损害,因为网站可能未能针对其配置进行测试。

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

有了用户代理客户端提示,浏览器就能以更注重隐私保护的方式访问相同的信息,进而降低用户代理字符串默认广播所有内容的默认设置。客户端提示会强制执行一个模型,在该模型中,服务器必须请求浏览器提供有关客户端的一组数据(提示),然后浏览器会应用自己的政策或用户配置来确定返回哪些数据。这意味着,我们不再默认公开所有用户代理信息,而是现在以明确且可审核的方式管理访问权限。开发者也可以受益于更简单的 API - 不再需要正则表达式!

目前的一系列客户端提示主要介绍浏览器的显示和连接功能。您可以在使用客户端提示自动选择资源中深入了解相关内容,但您可以在下面快速回顾一下该过程。

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

🇨?️ 服务器的响应

Accept-CH: Viewport-Width, Width

或元标记:

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

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

⬆️ 后续请求

Viewport-Width: 460
Width: 230

服务器可以选择改变其响应,例如,以适当的分辨率传送图片。

用户代理客户端提示会扩展具有 Sec-CH-UA 前缀的属性范围,该前缀可通过 Accept-CH 服务器响应标头指定。如需了解所有详情,请从解释器开始,然后深入了解完整方案

Chromium 89 中的用户代理客户端提示

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

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

⬆️ 所有请求

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"

用户代理响应和请求标头

🇦?️ 响应 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 页面,并发送其默认的基本用户代理。

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

除了标头之外,您还可以在 JavaScript 中通过 navigator.userAgentData 访问用户代理。您可以通过 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 标头指定。如需将其应用于用户代理客户端提示,您需要将提示转换为小写形式,并移除 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)来使用用户代理客户端提示,那么就应该重构此类实例。

接下来,您应重新检查自己对用户代理信息的使用情况,并尽可能将其替换为其他方法。通常,您可以使用渐进式增强功能、功能检测或自适应设计来实现相同的目标。依赖用户代理数据的基本问题是,始终要维护所检查属性与其启用的行为之间的映射。这是一项维护开销,可确保您的检测功能全面且保持最新状态。

考虑到这些注意事项,用户代理客户端提示代码库列出了一些网站的有效用例

用户代理字符串会怎样?

我们的计划是减少现有用户代理字符串公开的标识信息量,同时不对现有网站造成不当中断,从而最大限度地降低在网络上进行隐蔽跟踪的能力。现在,通过推出用户代理客户端提示,您可以在对用户代理字符串进行任何更改之前了解和试验新功能。

最终,用户代理字符串中的信息将会减少,因此它会保留旧格式,同时仅提供与默认提示相同的高级浏览器和重要版本信息。在 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

Sergey Zolkin 发布的 Un 拼图缩略图