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

用户代理客户端提示是对 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

上面的字符串包含有关用户的操作系统和版本、设备型号、浏览器品牌和完整版本的信息,以及足够的线索来推断出浏览器是移动浏览器,更不用说出于历史原因对其他浏览器进行过多次引用了。

这些参数与可能值的绝对多样性的组合意味着,用户代理字符串可以包含足够的信息来对单个用户进行唯一标识。如果您在 AmIUnique 处测试自己的浏览器,则可以了解您的用户代理字符串对“您”的识别程度。所得到的“相似度比率”越低,您的请求越独特,服务器就越容易暗中跟踪您。

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

此外,涉及用户代理字符串时,还需要提高 Web 兼容性。它不是结构化的,因此解析时会产生不必要的复杂性,这通常是导致用户体验 bug 和网站兼容性问题的原因。这些问题还会对不太常用的浏览器的用户造成严重影响,因为网站可能无法针对其配置进行测试。

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

用户代理客户端提示可让浏览器以更保护隐私的方式访问相同的信息,进而使浏览器最终能够减小用户代理字符串广播所有内容的默认设置。客户端提示会强制执行一个模型,在此模型中,服务器必须请求浏览器提供有关客户端的一组数据(提示),浏览器会应用自己的政策或用户配置来确定返回哪些数据。这意味着,现在以显式且可审核的方式管理访问权限,而不是默认公开所有用户代理信息。开发者还可以受益于更简单的 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

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

// 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)的实例,以改用 User-Agent Client Hints。

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

了解这些注意事项后,User-Agent Client Hints 代码库列出了一些适用于网站的有效用例

用户代理字符串会怎样?

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

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