1. 介绍
本节为非规范性内容。
目前,用户代理通常通过在每个请求中发送 User-Agent HTTP 请求头字段来向服务器标识自己(详见 [rfc9110]
的第5.5.3节)。理想情况下,这个头字段可以让服务器进行内容协商,针对特定用户代理精确地发送最能代表所请求资源的数据,从而优化带宽和用户体验。但实际上,这个头字段的值一方面作为默认设置暴露了过多关于用户设备的信息,另一方面又故意掩盖真实的用户代理,以规避服务器端的错误判定。
例如,最新版 iOS 上的 Chrome 标识为:
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X)
AppleWebKit/605.1.15 (KHTML, like Gecko)
CriOS/69.0.3497.105 Mobile/15E148 Safari/605.1
而最新版的 Edge 标识为:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.2704.79
Safari/537.36 Edge/18.014
这些字符串里包含了大量信息(也夹杂着不少虚假内容)。版本号、平台细节、型号信息等都会在每次请求时被广播,并成为各种指纹识别方案的基础。各浏览器厂商尝试过修改自己的用户代理字符串,但在历史实践中遇到了以下几类开发者反馈,阻碍了进一步改进:
-
品牌和版本信息(如 "Chrome 69")让网站能够绕过特定版本已知的 bug,这些 bug 无法通过其他方式检测。例如,不同厂商对内容安全策略的实现差异很大,难以在不知道具体浏览器的情况下确定应该在 HTTP 响应中发送什么策略。
-
开发者常常会根据用户代理和平台协商发送何种内容。例如,某些应用框架会在 iOS 和 Android 上采用不同样式,以匹配各自平台的美学和设计规范。
-
类似于第1点,操作系统版本和架构可能导致特定 bug,可以通过网站代码进行规避,同时也方便如下载合适的可执行文件(32位/64位,ARM/Intel等)。
-
高级开发者会利用型号/厂商信息来定制网站以适配设备能力(如 [FacebookYearClass]),并定位某些只在特定型号/厂商出现的性能问题和回归。
本文件提出了一种机制,允许用户代理更积极地减少 User-Agent 字符串中的熵,让真正需要客户端细节的服务器可以主动选择接收相关信息。它引入了多个新的客户端提示([RFC8942]),可提供客户端的品牌和版本信息、底层操作系统的品牌和主版本,以及底层设备的细节。用户代理不再向所有人一直广播这些数据,而是可以针对特定站点的需求,合理决定是否提供更细粒度的数据,从而减少网络暴露的
被动指纹识别面(参见 最佳实践1,[FINGERPRINTING-GUIDANCE])。
1.1. 示例
本节为非规范性内容。
用户首次使用最新版 "Examplary Browser" 访问 https://example.com/。他们的用户代理在 HTTP 请求中发送如下请求头:
Sec-CH-UA: "Examplary Browser"; v="73", ";Not?A.Brand"; v="27" Sec-CH-UA-Mobile: ?0 Sec-CH-UA-Platform: "Windows"
服务器希望根据用户的平台版本渲染内容,因此通过在初始响应中发送 Accept-CH 头([RFC8942] 第2.2.1节)请求更多信息:
Accept-CH: Sec-CH-UA-Platform-Version
随后,用户代理在下一个请求中加入了平台版本信息:
Sec-CH-UA: "Examplary Browser"; v="73", ";Not?A.Brand"; v="27" Sec-CH-UA-Mobile: ?0 Sec-CH-UA-Platform: "Windows" Sec-CH-UA-Platform-Version: "14.0.0"
1.2. 使用场景
本节为非规范性内容。
本节尝试记录当前 User-Agent 字符串的用途,以及如何通过 User-Agent 客户端提示(UA-CH)实现类似功能。
1.2.1. 差异化服务
1.2.1.1. 基于浏览器特性
此场景可让 polyfill.io 等服务针对用户定制 polyfill,而无需让现代浏览器用户的体验变得臃肿。同理,在为用户提供 Javascript 时,可以避免为支持最新 ES 特性的浏览器进行转译(否则会造成代码臃肿和低效)。在图片服务方面,有些浏览器不会更新 Accept 请求头,而 MIME
类型也可能不足以区分同一格式的不同变体(如 WebP)。此时,了解浏览器及其版本对于提供正确的图片变体至关重要。
要实现上述用例,服务器需要知道浏览器及其有效版本,并将其映射到可用功能列表,从而决定提供哪种 polyfill 或代码变体。
希望通过 UA-CH 实现此功能的服务需要检查每个请求中默认发送的 Sec-CH-UA 头,并据此调整响应。
1.2.1.2. 浏览器缺陷兼容处理
某些浏览器版本存在已知 bug,内容需要针对性地规避这些问题。触发这些 bug 会导致浏览器崩溃、内容损坏等问题,而这些 bug 本质上无法通过特性检测发现,因此内容需针对受影响的浏览器版本进行规避。在此用例中,服务器需知晓浏览器及其有效版本,了解相关 bug,并在当前浏览器版本受影响时应用兼容处理。
希望通过 UA-CH 实现此功能的服务需要检查每个请求中默认发送的 Sec-CH-UA 头,并据此调整响应。
1.2.2. 市场份额分析
浏览器的市场份额极其重要。了解某浏览器的使用情况可以激励开发者进行兼容性测试,减少用户遇到的问题。同时,浏览器的市场份额也直接影响浏览器厂商的商业目标,推动浏览器未来发展。要进行市场份额分析,服务器需了解以下一项或多项信息:用户代理名称及其有效版本、操作系统及其版本、设备型号。服务器可注册这些信息并统计其相对市场份额。
希望通过 UA-CH 提供市场份额分析的网站需检查每个请求中默认发送的 Sec-CH-UA 头,并进行记录。根据具体场景,还可请求其他 UA 客户端提示(如移动设备型号分析)。
设计上,品牌列表中的单项难以区分不太流行浏览器的真实品牌名和更流行浏览器的虚构品牌(GREASE)。由于不太流行的浏览器可能会为兼容性目的包含多个流行品牌名,因此若采用此方法,其用户很可能被归类为流行浏览器用户,导致市场份额统计偏向流行浏览器,而不太流行的浏览器则难以获得可见度。
因此,分析时最好将品牌列表作为一个整体,与各(浏览器,版本)组合发送的已知品牌列表进行比较。需要定期更新已知品牌列表,否则所有浏览器都将被归类为未知浏览器。但由于这不会影响用户访问网站,在此场景下未知浏览器统计归零是可以接受的。
可由中心化机构维护品牌列表(如 caniuse 和 MDN 维护浏览器特性支持,供众多站长使用)。
规范建议浏览器每个版本发送固定品牌列表,简化统计(也利于缓存),这样已知品牌列表只需映射到(浏览器,版本)组合。
1.2.3. 内容适配
内容适配即确保用户获得针对其需求定制的内容。内容适配不仅限于 UA 字符串,还涉及 多种维度:视口尺寸、设备内存、用户偏好等。本小节主要讨论依赖当前User-Agent 字符串信息的内容适配需求。
1.2.3.1. 基于浏览器的适配
有些网站会针对不同浏览器提供略有不同的内容,原因多种多样。有些理由是合理的(如根据功能支持提供不同体验),有些理由不太合理(如提醒用户未在某浏览器测试过),还有些理由是明显错误的(如只因浏览器类型而屏蔽用户访问)。作为浏览器,我们希望支持前者,抑制后者。
1.2.3.2. 移动端专用网站
许多网站会区分移动端和桌面端内容。响应式设计已让单一代码适配多种设备成为可能,但在某些场景下,移动端专版可能更具针对性。在这些场景下,向移动设备用户提供移动端专版网站是有帮助的。为实现此功能,服务器需在 HTML 响应阶段知晓用户是否为移动设备。
希望通过 UA-CH 实现移动端专版的网站可利用每个请求默认发送的 Sec-CH-UA-Mobile 头。
1.2.3.3. 低性能设备
有些网站会针对低性能设备(难以处理高负载任务、大视频和大图片等)提供不同内容。此类适配通常依赖User-Agent 字符串里的设备型号信息,并结合服务器端数据库将型号转换为内存、CPU
等类别进行内容划分。
若划分维度为内存,可用 Device-Memory 客户端提示。使用 UA-CH 时,网站可通过 Sec-CH-UA-Model 获取设备型号。
这两种提示均非默认发送,因此需要额外配置。
顶级域需在响应中发送 Accept-CH: Device-Memory, Sec-CH-UA-Model 头以接收相关提示。若需每次导航都适配,可在缺少提示时通过重定向解决,或用
Critical-CH 让客户端处理额外请求响应流程。
第三方域若需适配,需顶级域进行 跨域提示授权。顶级域需配置
Accept-CH 并通过 Permissions-Policy 头授权相关提示给第三方。
1.2.3.4. 操作系统专属样式
一些网站希望界面风格能匹配用户的操作系统。渐进增强通常是更优选择(如用脚本应用不同按钮样式),但有时会希望基于平台和平台版本提供定制的内联样式。这类场景与“低性能设备”问题类似,只是用的是 Sec-CH-UA-Platform 和 Sec-CH-UA-Platform-Version 提示。
1.2.3.5. 操作系统集成
同样,有些网站希望将链接换为操作系统专用(如 Android intent 链接)。渐进增强可以用脚本修改这些链接,但部分网站偏好服务器端适配。同“操作系统专属样式”,需用 Sec-CH-UA-Platform 和 Sec-CH-UA-Platform-Version 实现。
1.2.3.6. 浏览器和操作系统专属实验
有些服务器可能只希望在特定浏览器、特定平台或其版本上进行多变体实验。若仅限浏览器及版本,网站可用默认的Sec-CH-UA。如需平台及其版本,可用默认的
Sec-CH-UA-Platform,但需请求 Sec-CH-UA-Platform-Version,或用客户端脚本控制实验。
1.2.4. 用户登录通知
很多网站,尤其是安全敏感类网站,都会在新设备登录时通知用户。这样用户能及时知晓并在发现异常时采取措施。为使通知有意义,网站需识别并告知用户所用浏览器的商业品牌信息。通常还会包括平台及其版本,以确保用户明确是哪台设备。
此类通知无需服务器端适配,因此建议使用 userAgentData.getHighEntropyValues() 方法获取所需信息。
1.2.5. 下载合适的二进制可执行文件
部分网站用于下载本地应用的二进制可执行文件,需能默认向用户推荐合适的可执行文件。合适的文件需考虑多个因素:操作系统、版本、位宽及 CPU 架构等。为满足该用例,下载类网站可主动接收
Sec-CH-UA-Platform、Sec-CH-UA-Platform-Version、Sec-CH-UA-Arch 和
Sec-CH-UA-Bitness 提示(或通过 API 查询),以确保默认推荐合适的文件。
1.2.6. 转化建模
某些机器学习模型会用User-Agent 字符串中的细节来估算用户属性。使用客户端提示也能实现类似建模,但需明确选择收集相关信息。
1.2.7. 漏洞过滤
某些环境下,代理服务器会校验用户是否使用存在安全漏洞的过时设备。虽然Sec-CH-UA 能提供部分信息,但浏览器和操作系统的完整版本信息对于该类分析尤为重要。
此类代理需增加一次重定向,或用两种 客户端提示可靠性机制来主动获取浏览器完整版本和平台版本,以保证能持续获取相关提示。
1.2.8. 日志与调试
许多服务目前会记录User-Agent 字符串,并在分析流量或排查服务错误时加以利用。这些服务需用 Sec-CH-UA
提供的低熵值进行日志记录,或主动接收高熵提示。后者仅在特定问题时建议使用。遇到特殊问题时可主动获取更多用户代理细节,或用 userAgentData.getHighEntropyValues()
API 实现。
1.2.9. 指纹识别
用户指纹识别是指收集用户的多项信息并组合形成唯一签名,从而即使用户清理浏览器状态(如删除 cookie)也能再次识别其身份。
此类场景下,源站会收集尽可能多的熵,因此可能会请求所有可用提示。
1.2.9.1. 垃圾过滤和机器人检测
这是指纹识别的一种非恶意用途,因此我们希望予以保留。通过 UA-CH,初期可主动收集各类提示实现此用例。我们希望未来有替代方法或 API 能解决垃圾过滤和机器人检测场景,因为浏览器可能会介入限制用户身份熵收集(如 隐私预算提案)。
1.2.9.2. 持久化用户追踪
这是本提案明确试图加大难度的指纹识别场景。和“垃圾过滤”类似,仍可主动收集所有提示作为熵值。但与上述用例不同,隐私预算等提案旨在阻止持久化追踪,而不会为持久化追踪提供替代方案。1.2.9.3. 拦截已知机器人和爬虫
当前,User-Agent 字符串常被用作阻止已知机器人和爬虫的简单手段。有担忧认为默认减少熵暴露后,机器人更容易隐藏于“人群”之中。虽然有一定道理,但不应因此让普通用户更易被识别。
和垃圾过滤场景类似,希望未来有替代方法能替代 User-Agent 匹配实现此用例。
2. 基础设施
本规范依赖客户端提示基础设施、HTTP 客户端提示、Infra 标准和权限策略。[CLIENT-HINTS-INFRASTRUCTURE] [RFC8942] [INFRA] [permissions-policy-1]
部分术语定义参见 用于 HTTP 的结构化字段值。[rfc9651]
3. 用户代理提示
以下章节定义了多个 HTTP 请求头字段,用于暴露特定用户代理的详细信息,服务器可通过 [RFC8942] 中定义的客户端提示基础设施主动选择接收这些信息。下面的定义假设每个用户代理为自身定义了一些属性:
-
品牌 - 用户代理的商业名称(例如:"cURL"、"Edge"、"The World’s Best Web Browser"),长度必须小于32个ASCII 字母字符。
-
形态因子 - 设备的形态因子,历史上通过 User-Agent 字符串中的 <deviceCompat> token 表示(如 "Tablet"、"VR" 等)。
-
完整版本 - 与用户代理或其品牌列表中的任一品牌相关的构建版本(如:"72.0.3245.12"、"3.14159" 或 "297.70E04154A")。
-
型号 - 用户代理的设备型号(如:"" 或 "Pixel 2 XL")。
-
移动性 - 一个布尔值,表示用户代理的设备是否为移动设备。(如:?0 或 ?1)
-
平台品牌 - 用户代理的操作系统商业名称。(如:"Windows"、"iOS" 或 "AmazingOS")
-
平台版本 - 用户代理的操作系统版本。(如:"NT 6.0"、"15" 或 "17G")
-
平台架构 - 用户代理的底层 CPU 架构(如:"ARM" 或 "x86")。
-
平台位宽 - 用户代理的底层 CPU 架构位宽(如:"32" 或 "64")。
-
重要版本 - 包含可区分 Web 暴露特性的营销版本(如:"72"、"3" 或 "12.1"),与用户代理或其品牌列表中的任一品牌相关(如渲染引擎或其它等价类完整版本)。
-
wow64 属性 - 一个布尔值,表示用户代理的二进制文件是否在 64 位 Windows 的 32 位模式下运行。(如:?0 或 ?1)
每当针对给定的 环境设置对象 environment settings 请求 Client Hints 时,如果与 environment settings 相关联的 模拟 UA Client Hints 不为 null,则返回 模拟 UA Client Hints 中对应的值,而不是所请求属性的默认值。
用户代理 应保持这些字符串简短且直观,但服务器必须接受任意值,因为这些值都是由 用户代理 随意构造的。
-
x86 CPU 架构 => "x86"
-
ARM CPU 架构 => "arm"
其他 CPU 架构可以在有意义的情况下映射为上述值之一,或映射为空字符串。
用户代理 应该为 平台架构 或 平台位数 返回空字符串或虚构值,除非用户的平台同时满足以下两个条件:
-
可执行文件的二进制下载很有可能。
-
不同 CPU 架构很可能需要不同的二进制执行资源,并且不同的二进制执行资源很可能是可用的。
用户代理 必须为 型号 返回空字符串,如果 移动性 为 false。用户代理 即使 移动性 为 true,也必须为 型号 返回空字符串,除非该平台通常暴露型号信息。
用户代理
可为类型为 sf-string 的提示返回空字符串,为类型为 sf-boolean 的提示返回
false,或因隐私、兼容性等原因返回其它虚构值,对以下任意提示进行请求时:完整版本、平台架构、平台位数、wow64-性、或 型号。
如果 用户代理 的 模拟客户端提示 不为 null,则 用户代理 必须返回 模拟客户端提示 的对应值,而不是所请求属性的默认值。
3.1. ‘Sec-CH-UA’ 请求头字段
Sec-CH-UA 请求头字段为服务器提供关于 用户代理的 品牌和 重要版本号的信息。它是一个 结构化请求头,其值必须为一个 列表 [rfc9651]。列表中的每个项目必须为 字符串。每个项目的值应包含一个
"v" 参数,表示 用户代理的版本。
该请求头的 ABNF 定义为:
Sec-CH-UA = sf-list
要为请求返回 Sec-CH-UA
值,需执行以下步骤:
注意:与大多数 Client Hints 不同,由于该字段被包含在 低熵提示表 中,无论服务器是否通过 Accept-CH
请求头选择接收该字段,Sec-CH-UA 请求头都会被默认发送(但仍可由其 策略控制的客户端提示功能控制)。该字段被认为是低熵的,因为它仅包含 用户代理的品牌信息与重要版本号(两者均可通过观察其他头部结构或检测浏览器新旧特性明确推测 [Janc2014])。
注意:Sec-CH-UA 会暴露 品牌列表中每个品牌的主版本号。如需完整版本用例,请参见 Sec-CH-UA-Full-Version-List。
3.2. 'Sec-CH-UA-Arch' 头字段
Sec-CH-UA-Arch 请求头字段向服务器提供有关特定 用户代理
所运行平台的架构信息。它是一个
结构化头,其值必须为
字符串
[rfc9651]。
该头的 ABNF 如下:
Sec-CH-UA-Arch = sf-string
3.3. 'Sec-CH-UA-Bitness' 头字段
Sec-CH-UA-Bitness 请求头字段向服务器提供有关
该 平台位数 的信息,即该
用户代理
所运行平台架构的位数。
它是一个 结构化头部(Structured Header),其值必须为 字符串
[rfc9651]。
该头的 ABNF 为:
Sec-CH-UA-Bitness = sf-string
3.4. Sec-CH-UA-Form-Factors 头字段
Sec-CH-UA-Form-Factors 请求头字段向
服务器提供该 用户代理 的
交互形态(form-factors) 信息。
它是一个 结构化头部,其值必须为 列表
[rfc9651]。为避免产生额外的指纹熵,
该头部的值必须采用词典序并区分大小写。
该头应当用如下常见交互形态值之一或多个描述设备的形态:"Desktop"、"Automotive"、"Mobile"、"Tablet"、"XR"、"EInk" 或 "Watch"。应当包含所有适用的形态值。
用户代理的交互形态描述了用户如何与用户代理进行交互。允许值的含义如下:
-
"Desktop" 指在个人电脑上运行的用户代理。
-
“Automotive” 指嵌入在车辆中的用户代理,用户可能需要负责驾驶车辆,无法兼顾小细节。
-
"Mobile" 指小型、以触控为主、通常随身携带的设备。
-
"Tablet" 指尺寸大于“Mobile”的触控设备,通常不随身携带。
-
"XR" 指增强或替换用户周围环境的沉浸式设备。
-
"EInk" 指以慢屏刷新和有限或无色彩分辨率为特征的设备。
-
"Watch" 指屏幕极小(通常小于2 英寸),可让用户快速瞥一眼的移动设备。
如有一种新的交互形态,用户以有显著区别的方式与其交互,或站点需要基于该交互形态改变与用户的交互方式,且现有提示无法可靠识别该形态时,应提出新值并加入规范。
该头的 ABNF 为:
Sec-CH-UA-Form-Factors = sf-list
3.5. 'Sec-CH-UA-Full-Version' 头字段
Sec-CH-UA-Full-Version 已废弃,并将在将来移除。开发者应改用
Sec-CH-UA-Full-Version-List。
Sec-CH-UA-Full-Version 请求头字段向服务器提供有关
用户代理 完整版本
的信息。它是一个 结构化头,其值必须为 字符串 [rfc9651]。
该头的 ABNF 为:
Sec-CH-UA-Full-Version = sf-string
3.6. Sec-CH-UA-Full-Version-List 头字段
Sec-CH-UA-Full-Version-List
请求头字段向服务器提供
brands 列表中每个品牌的
完整版本
信息。它是一个 结构化头,其值必须为 列表 [rfc9651]。
该头的 ABNF 为:
Sec-CH-UA-Full-Version-List = sf-list
要 返回指定请求的
Sec-CH-UA-Full-Version-List 值,请执行以下步骤:
-
令 brands 为以 "full version" 作为参数运行 create brands 的结果。
-
令 list 为以 brands 和 "full version" 作为参数运行 create a brand-version list 的结果。
-
返回以 list 作为输入运行 serializing a list 的输出。
3.7. Sec-CH-UA-Mobile 头字段
Sec-CH-UA-Mobile 请求头字段向服务器提供
给定 用户代理 是否偏好“移动端”用户体验的信息。它是一个 结构化头,
其值必须为 布尔值 [rfc9651]。
该头的 ABNF 为:
Sec-CH-UA-Mobile = sf-boolean
Note: 类似 Sec-CH-UA,因其在
低熵提示表中,所以默认会发送 Sec-CH-UA-Mobile 头,无论服务器是否通过
Accept-CH 选择接收(当然也可以通过
受策略控制的 Client Hints 功能 控制发送)。
它属于低熵信息,因为它是用户可直接控制的单个比特。
3.8. Sec-CH-UA-Model 头字段
Sec-CH-UA-Model 请求头字段向服务器提供
给定 用户代理 所运行的设备信息。它是一个 结构化头,其值必须为 字符串
[rfc9651]。
该头的 ABNF 为:
Sec-CH-UA-Model = sf-string
3.9. Sec-CH-UA-Platform 头字段
Sec-CH-UA-Platform 请求头字段向服务器提供
给定 用户代理 当前运行的平台信息。它是一个 结构化头,
其值必须为 字符串 [rfc9651]。其值应为如下常见平台值之一:
"Android"、"Chrome OS"、"Fuchsia"、"iOS"、"Linux"、"macOS"、"Windows" 或 "Unknown"。
该头的 ABNF 为:
Sec-CH-UA-Platform = sf-string
Note: 类似 Sec-CH-UA,因其在
低熵提示表中,所以默认会发送 Sec-CH-UA-Platform 头,无论服务器是否通过
Accept-CH 选择接收(当然也可以通过
受策略控制的 Client Hints 功能 控制发送)。
3.10. Sec-CH-UA-Platform-Version 头字段
Sec-CH-UA-Platform-Version 请求头字段向服务器提供
给定 平台版本 的信息,用户代理正运行于该平台。它是一个 结构化头,其值必须为 字符串[rfc9651]。
其值为以 get the
platform version 调用 平台品牌 得到的结果。
要 获取平台版本,给定字符串 platform,请执行以下步骤:
-
如果 platform 为 "Linux" 或 "Fuchsia":
-
返回空字符串。
-
-
如果 platform 为 "Android":
-
令 platformReturnedVersionString 为操作系统查询
android.os.Build.VERSION.RELEASE字符串的结果。 -
以 platformReturnedVersionString 作为参数调用 创建统一平台版本字符串并返回结果。
-
-
如果 platform 为 "iOS":
-
令 platformReturnedVersionString 为
currentDevice返回的UIDevice对象的systemVersion属性值。 -
以 platformReturnedVersionString 作为参数调用 创建统一平台版本字符串并返回结果。
-
-
如果 platform 为 "Windows":
-
如果可用(如 Windows 10 及以上),令 platformReturnedVersionString 为查询
Windows.Foundation.UniversalApiContract整数版本并转为字符串的结果。否则,令 platformReturnedVersionString 为 获取传统 Windows 版本号 的结果。 -
以 platformReturnedVersionString 作为参数调用 创建统一平台版本字符串并返回结果。
-
-
令 platformVersionComponentList 为一个 列表。
-
如果 platform 为 "macOS":
-
令 macOSVersion 为获取
processInfo信息代理返回的NSProcessInfo对象的operatingSystemVersion属性。 -
Append macOSVersion 的
majorVersion、minorVersion和patchVersion组件(按顺序)到 platformVersionComponentList。
-
-
如果 platform 为其他值:
-
返回以 U+002E FULL STOP(
.)为分隔符对 platformVersionComponentList 进行连接的结果。
要 获取传统 Windows 版本号,请执行以下步骤:
-
令 major 为 Win32
GetVersionExAPI 返回的OSVERSIONINFO的dwMajorVersion成员值。 -
令 minor 为 Win32
GetVersionExAPI 返回的OSVERSIONINFO的dwMinorVersion成员值。 -
若 major 为
6且 minor 为3(即 Windows 8.1),返回 "0.3"。 -
若 major 为
6且 minor 为2(即 Windows 8),返回 "0.2"。 -
若 major 为
6且 minor 为1(即 Windows 7),返回 "0.1"。 -
否则,返回 "0"。
要 创建统一平台版本字符串,给定字符串 input,请执行以下步骤:
-
令 platformVersionComponentList 为一个 列表,index 为 0。
-
令 platformVersionUnprocessedTokenList 为以 U+002E FULL STOP(
.)对 input 执行 严格分割 得到的 列表。 -
当 index 小于 3 时:
-
返回以 U+002E FULL STOP(
.)分隔的 platformVersionComponentList 串联结果。
该头的 ABNF 为:
Sec-CH-UA-Platform-Version = sf-string
3.11. 'Sec-CH-UA-WoW64' 头字段
Sec-CH-UA-WoW64 请求头字段提供有关给服务器
指定的 用户代理 二进制是否在 64 位 Windows 上以 32 位模式运行的信息。
它是一个 结构化头部,其值必须为 布尔值 [rfc9651]。
该头的 ABNF 为:
Sec-CH-UA-WoW64 = sf-boolean
Note: 这些 client hints 可通过以下 client hints tokens 激活:
Sec-CH-UA, Sec-CH-UA-Arch, Sec-CH-UA-Bitness,
Sec-CH-UA-Form-Factors,
Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List,
Sec-CH-UA-Mobile, Sec-CH-UA-Model,
Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-WoW64
4. 接口
dictionary {NavigatorUABrandVersion DOMString ;brand DOMString ; };version dictionary {UADataValues DOMString ;architecture DOMString ;bitness sequence <NavigatorUABrandVersion >;brands sequence <DOMString >;formFactors sequence <NavigatorUABrandVersion >;fullVersionList DOMString ;model boolean ;mobile DOMString ;platform DOMString ;platformVersion DOMString ; // deprecated in favor of fullVersionListuaFullVersion boolean ; };wow64 dictionary {UALowEntropyJSON sequence <NavigatorUABrandVersion >;brands boolean ;mobile DOMString ; }; [platform Exposed =(Window ,Worker )]interface {NavigatorUAData readonly attribute FrozenArray <NavigatorUABrandVersion >;brands readonly attribute boolean ;mobile readonly attribute DOMString ;platform Promise <UADataValues >(getHighEntropyValues sequence <DOMString >);hints UALowEntropyJSON (); };toJSON interface mixin { [NavigatorUA SecureContext ]readonly attribute NavigatorUAData ; };userAgentData Navigator includes NavigatorUA ;WorkerNavigator includes NavigatorUA ;
Note: 用户代理信息中的高熵部分是通过 Promise
进行获取的,以便让 用户代理 有机会在暴露这些信息前进行可能耗时的检查(如请求用户许可)。
4.1. 处理模型
4.1.1. WindowOrWorkerGlobalScope
每个 用户代理 关联一个 brands,它是通过以 create brands 使用 significant version 创建的 列表。
每个 WindowOrWorkerGlobalScope
对象都关联有一个
brands frozen array,该数组类型为
FrozenArray<NavigatorUABrandVersion>。
它最初是通过对 用户代理 的
brands 执行 create frozen array 得到的。
此外,每个 WindowOrWorkerGlobalScope
对象还关联有一个
full version list frozen array,
类型为
FrozenArray<NavigatorUABrandVersion>。
它通过以 create brands 使用
full
version 的结果创建 frozen array 得到。
4.1.2. 创建 brands
当被要求以 create brands 带参数 version type 时,执行如下步骤:
-
令 list 为一个 列表。
-
断言 version type 是 "full version" 或 "significant version"。
-
对于代表 brand 的 用户代理 —— 或等价类 —— 作为 brand:
-
令 version 为 字符串,按如下初始化:
-
若 version type 为 "full version",则 version 为 full version 字符串。
-
若 version type 为 "significant version",则 version 为 significant version 字符串。
-
-
令 dict 为一个新的
NavigatorUABrandVersion字典,brand设为 brand,version设为 version。 -
追加 dict 到 list。
-
-
用户代理 应执行如下操作:
-
追加 额外一项包含
NavigatorUABrandVersion字典到 list,该字典的brand由 arbitrary brand 设置,version为 create an arbitrary version 的结果。 -
随机排列 list 里的各项顺序。
Note: 为减少生成这些随机组件时的缓存差异,可以在 build 时确定并贯穿用户代理的 significant version 生命周期。
Note: 参见 § 8.2 GREASE-like UA Brand Lists,了解这些随机化步骤何时以及为何适用。
-
-
返回 list。
equivalence class 表示一组认为彼此兼容的浏览器。例如,共享渲染引擎可组成 equivalence class。
4.1.3. 创建任意 brand 和 version 值
为 create an arbitrary brand,用户代理 必须执行:
-
令 arbitraryBrand 为一个由 ASCII alpha 和 0x20 (SP) 组成的 字符串。 arbitraryBrand 必须至少包含一个 0x20 (SP),且长度不超过 20 个 ASCII 字节,且不能以 0x20 (SP) 开头或结尾。
-
令 arbitraryBrandList 为对 arbitraryBrand 进行 ASCII 空白分割的结果。
-
令 greaseyStack 为 栈。
-
令 greaseyChars 为 列表,内容为 ASCII 字节« 0x20 (SP)、0x28 (左括号)、0x29 (右括号)、0x2D (-)、0x2E (.)、0x2F (/)、0x3A (:)、0x3B (;)、0x3D (=)、0x3F (?)、0x5F (_) »。
-
令 index 为 0。
-
当 index 小于 arbitraryBrandList 大小 - 1:
-
Push 随机选自 greaseyChars 的一项到 greaseyStack。
-
index 加 1。
-
-
令 greaseyBrandList 为 列表,将 index 设为 0。
-
当 greaseyStack 非空:
-
追加 arbitraryBrandList[index] 到 greaseyBrandList。
-
返回 去除首尾 ASCII 空白 后的 greaseyBrandList 字符串拼接 结果(不加分隔符)。
此外,尽管 结构化头部 允许在 字符串里转义 0x22 (\") 和 0x5C (\\),但这些字符也会导致防火墙兼容性问题。
为 create an arbitrary version (参数为 version type),执行:
-
断言 version type 是 "full version" 或 "significant version"。
-
令 arbitrary version 为字符串,按如下初始化:
-
若 version type 为 "full version",则 arbitrary version 为与 full version 格式一致但不等值的字符串。
-
若 version type 为 "significant version",则 arbitrary version 为与 significant version 格式一致但不等值的字符串。
-
-
返回 arbitrary version。
Note: 用户代理可选择发送任意较低版本以确保版本检测正确,并应随时间变更。
4.1.4. 创建品牌-版本列表
要创建品牌-版本列表,给定brands和version type,执行以下步骤:
4.1.5. Getter方法
读取时,brands
属性必须返回 此对象的 相关全局对象的 brands frozen array。
读取时,platform
属性必须返回 用户代理的 平台品牌。
4.1.6. getHighEntropyValues 方法
getHighEntropyValues(hints)
方法必须执行如下步骤:
-
令 uaData 为一个新的
UADataValues。 -
将 uaData["
brands"] 设置为 此对象的 相关全局对象的 brands frozen array。 -
如果 此对象的 相关全局对象的 关联文档不被 允许使用 ch-ua-high-entropy-values 功能,则用 uaData 解析 p。
-
否则,并行运行以下步骤:
-
如果 hints 包含 "architecture",将 uaData["
architecture"] 设置为 用户代理的 平台架构。 -
如果 hints 包含 "formFactors",将 uaData["
formFactors"] 设置为 用户代理的 交互形态。 -
如果 hints 包含 "fullVersionList",将 uaData["
fullVersionList"] 设置为 此对象的 相关全局对象的 full version list frozen array。 -
如果 hints 包含 "platformVersion",将 uaData["
platformVersion"] 设置为以 平台品牌 为参数运行 获取平台版本 的结果。 -
如果 hints 包含 "uaFullVersion",将 uaData["
uaFullVersion"] -
在 permission task source 上 调度任务,用 uaData 解析 p。
-
-
返回 p。
4.1.7. toJSON 方法
toJSON() 方法必须执行如下步骤:
-
令 uaLowEntropyData 为一个新的
UALowEntropyJSON -
将 uaLowEntropyData["
brands"] 设置为 此对象的 相关全局对象的 brands frozen array。 -
返回 uaLowEntropyData
5. 自动化
5.1. 定义
UserAgentClientHintsCommand = ( emulation.SetClientHintsOverrideCommand )
品牌版本 是一个 结构体,包含如下内容:
用户代理客户端提示 是一个 结构体,包含如下内容:
-
项 名为 full version,类型为字符串或 null;
-
项 名为 mobile,类型为布尔值或 null;
-
项 名为 model,类型为字符串或 null;
-
项 名为 platform,类型为字符串或 null;
-
项 名为 platform version,类型为字符串或 null;
-
项 名为 architecture,类型为字符串或 null;
-
项 名为 bitness,类型为字符串或 null;
-
项 名为 wow64,类型为布尔值或 null。
-
项 名为 default emulated client hints, 类型为 用户代理客户端提示 或 null,初始值为 null;
-
项 名为 emulated client hints per user contexts, 类型为 用户上下文 到 用户代理客户端提示 的弱映射,初始为空;
-
项 名为 emulated client hints per navigables, 类型为 可导航项 到 用户代理客户端提示 的弱映射,初始为空。
5.2. emulation.setClientHintsOverride 命令
emulation.setClientHintsOverride 命令用于为一组 可导航项 或 用户上下文,或全局范围设置或移除模拟的用户代理客户端提示。
- 命令类型
-
emulation.SetClientHintsOverrideCommand= {method:"emulation.setClientHintsOverride",params: {clientHints: emulation.ClientHintsMetadata / null, ?contexts: [+text], ?userContexts: [+text], } }emulation.ClientHintsMetadata= { ?brands: [* emulation.BrandVersion], ?fullVersionList: [* emulation.BrandVersion], ?platform: text, ?platformVersion: text, ?architecture: text, ?model: text, ?mobile: bool, ?bitness: text, ?wow64: bool, ?formFactors: [* text]; }emulation.BrandVersion= {brand: text,version: text } - 返回类型
-
emulation.SetClientHintsOverrideResult= {}
-
令 related navigables 为以 environment settings 调用 get related navigables 的结果。
-
对于每个 navigable 或 related navigables:
-
令 top-level navigable 为 navigable 的 顶层 traversable。
-
令 user context 为 top-level navigable 的 关联用户上下文。
-
如果 模拟客户端提示 的 emulated client hints per navigables 包含 top-level navigable,则返回 模拟客户端提示 的 emulated client hints per navigables[top-level navigable]。
-
如果 模拟客户端提示 的 emulated client hints per user contexts 包含 user context,则返回 模拟客户端提示 的 emulated client hints per user contexts[user context]。
-
-
令 default emulated client hints 为 模拟客户端提示 的 default emulated client hints。
-
如果 default emulated client hints 非 null,返回 default emulated client hints。
-
返回 null。
给定 command parameters,远端步骤如下:
-
如果 command parameters 包含 "
userContexts" 且 command parameters 包含 "contexts", 则返回带有 错误,错误码为 error code invalid argument。 -
令 emulated client hints 为 command parameters["
clientHints"]。 -
如果 command parameters 包含 "
contexts": -
令 navigables 为以 command parameters["
contexts"] 调用 try get valid top-level traversables by ids 的结果。 -
对于 navigables 中的每个 navigable:
-
如果 emulated client hints 为 null,移除 navigable 于 模拟客户端提示的 emulated client hints per navigables 中。
-
否则,设置 模拟客户端提示的 emulated client hints per navigables[navigable] 为 emulated client hints。
-
-
返回带有空数据的 成功。
-
如果 command parameters 包含 "
userContexts": -
令 user contexts 为以 command parameters["
userContexts"] 调用 try get valid user contexts 的结果。 -
对于 user contexts 中的每个 user context:
-
如果 emulated client hints 为 null,移除 user context 于 模拟客户端提示的 emulated client hints per user contexts 中。
-
否则,设置 模拟客户端提示的 emulated client hints per user contexts[user context] 为 emulated client hints。
-
-
返回带有空数据的 成功。
-
设置 模拟客户端提示的 default emulated client hints 为 emulated client hints。
-
返回带有空数据的 成功。
6. 权限策略集成
本规范定义了一个由字符串
"ch-ua-high-entropy-values"
标识的
策略控制特性,
它的 默认允许列表 为 '*'。
这用于决定文档是否允许通过 高熵 client hint 值
以及 getHighEntropyValues()
API 返回这些值。
注意: 如果某个文档不被允许使用
"ch-ua-high-entropy-values" 特性,
getHighEntropyValues() API 仍会为便利性继续返回低熵值。
7. 安全与隐私注意事项
7.1. 安全传输
Client Hints 不会发送到不安全端点(参见 [RFC8942] 第 2.2.1 节中的安全传输要求)。 这意味着 用户代理 信息不会通过明文渠道泄漏,从而减少了网络攻击者长期建立代理行为档案的机会。
7.2. 委托
Client Hints 会通过 Permissions Policy 从顶级页面进行委托 ([permissions-policy-1])。 这样可以减少 用户代理 信息随子资源请求被传递的可能, 进而减少了 被动指纹追踪 的风险。
该委托作为 append client hints to request 的一部分定义。
7.3. 指纹追踪
User Agent Client Hints 的主要目标是通过 User-Agent 头字段减少暴露给整个 web 的默认熵, 因该字段可能被用于 被动指纹追踪。
用户代理 可以决定提供哪些 hint, 理想情况下不会比未经裁剪的 User-Agent 头包含更多信息。 用户代理 可以为其不想提供的值返回空字符串,或完全拒绝返回该 hint。
然而,部分或全部 hints 仍然可能被请求并被一方或受委托的第三方用于 主动指纹追踪。如 § 7.4 访问限制 所述, 用户代理 应考虑通过策略限制或减少对 有主动指纹行为方的访问。
用户代理 应谨慎, 避免通过 GREASE-like brand 列表引入针对单一或极少用户的指纹向量。应采用一种策略,使该任意 brand 可被众多用户共享 (例如在同一主版本间所有用户保持稳定)。
7.4. 访问限制
上文定义的 Client Hints 揭示了大量关于用户代理及其运行设备的信息。 用户代理 在授予访问这些信息前应谨慎, 可施加超出安全传输和委托要求的额外限制。例如, 用户代理 可以选择仅在用于下载时 暴露 平台架构 或 平台位数, 让服务器可以选择合适二进制。同样,也可以让用户控制向服务器暴露哪些值,或通过权限提示/设置界面要求显式用户交互后才授予访问。
8. 实现注意事项
8.1. User-Agent 头
用户代理 应通过减少 User-Agent 头的信息细节以取代 Client Hints 模型中描述的方式,逐步弃用该头部的用法。 近期内该头部完全移除几乎不可能,因为现有站点的内容协商代码仍需依赖它(参见 [Rossi2015],关于新浏览器此处难题的示例)。
一种可建议的做法是每个 用户代理 固定其
User-Agent 头的值,通过永远包含 "like Gecko" 和 "AppleWebKit/537.36" 等旧声明来确保向后兼容。
这种方式可以逐步推进,先冻结版本号,再将平台和型号信息转为通用值,从而缩小该头部带来的指纹面积。
8.2. GREASE-like UA 品牌列表
历史经验表明,用户代理 为了让用户能通过某些站点嗅探脚本和避免被基于 UA 的允许/阻止列表阻拦,确实存在伪造品牌的动机。
短期内,重置预期也许有助于防止滥用 brands 列表,但长期效果有限。网络协议引入了 GREASE(参见 [I-D.ietf-tls-grease])的概念,我们可以借鉴该理念来应对本问题。
用户代理 的 brands 含多个条目可以推动 brands 列表的标准化处理。随机包含额外的、特意错误、顺序任意的条目,有助于避免依赖特定字符串的僵化。
以下为若干示例:
-
为避免站点禁止未知浏览器,Chrome 可发送包含一个不会存在的浏览器,且会随时间变动的 UA 集。
-
"Chrome"; v="73", "(Not;Browser"; v="12"
-
-
为基于 Chromium 版本实现等价类,Chrome 可将渲染引擎及其版本加入集合中。
-
"Chrome"; v="73", "(Not;Browser"; v="12", "Chromium"; v="73"
-
-
为鼓励站点基于 Chromium 版本的等价类而非精确 UA 嗅探,Chrome 可以完全将自己从集合移除。
-
"(Not;Browser"; v="12", Chromium"; v="73"
-
-
基于 Chromium 浏览器可采用类似 UA 字符串,但将自身品牌加入集合,从而使站点可识别和统计。
-
"Chrome"; v="73", "Xwebs mega"; v="60", "Chromium"; v="73", "(Not;Browser"; v="12"
-
用户代理 在 brands 中必须包含多于一个值,并且其中一个必须是任意值。
brands 的值顺序必须随时间变化,防止接收方依赖某些值在列表中的固定位置。
选择 GREASE 策略时,用户代理 应考虑缓存差异与分析等场景,尽量减少同版本 用户代理 的差异。
注意: 一个减少缓存和分析差异的做法是,在构建时确定 UA 集合的 GREASE 部分,并在同一重大版本期间保持不变。
8.3. 'Sec-CH-' 前缀
限制用户空间 JavaScript 影响和修改 UA-CH 头有安全方面的优势。同时,似乎没有合理的 用例 需要这样的用户空间重写。
因此,根据与 TAG 的讨论,禁止 JavaScript (如通过
fetch 或 Service Workers)对这些头部的写入访问是合理的,并将其明确为浏览器控制的 client hints,相关文档可直接包含入请求且不会触发 CORS 预检。
因此,本规范中定义的请求头都包含 Sec-CH- 前缀。
9. IANA 注意事项
本文件拟定义 Sec-CH-UA、Sec-CH-UA-Arch、Sec-CH-UA-Bitness、
Sec-CH-UA-Form-Factors、Sec-CH-UA-Full-Version、Sec-CH-UA-Mobile、
Sec-CH-UA-Model、
Sec-CH-UA-Platform、Sec-CH-UA-Platform-Version、Sec-CH-UA-WoW64 以及
Sec-CH-UA-Full-Version-List HTTP 请求头,并将其注册到长期消息头字段注册表
([RFC3864])。
本文件也拟废弃 User-Agent 头字段的使用。
9.1. 'Sec-CH-UA' 头字段
头字段名称: Sec-CH-UA
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.1 The 'Sec-CH-UA' Header Field)
9.2. 'Sec-CH-UA-Arch' 头字段
头字段名称: Sec-CH-UA-Arch
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.2 The 'Sec-CH-UA-Arch' Header Field)
9.3. 'Sec-CH-UA-Bitness' 头字段
头字段名称: Sec-CH-UA-Bitness
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.3 The 'Sec-CH-UA-Bitness' Header Field)
9.4. 'Sec-CH-UA-Form-Factors' 头字段
头字段名称: Sec-CH-UA-Form-Factors
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.4 The 'Sec-CH-UA-Form-Factors' Header Field)
9.5. 'Sec-CH-UA-Full-Version' 头字段
头字段名称: Sec-CH-UA-Full-Version
适用协议: http
状态: 已废弃
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.5 The 'Sec-CH-UA-Full-Version' Header Field)
9.6. 'Sec-CH-UA-Full-Version-List' 头字段
头字段名称: Sec-CH-UA-Full-Version-List
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.6 The 'Sec-CH-UA-Full-Version-List' Header Field)
9.7. 'Sec-CH-UA-Mobile' 头字段
头字段名称: Sec-CH-UA-Mobile
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.7 The 'Sec-CH-UA-Mobile' Header Field)
9.8. 'Sec-CH-UA-Model' 头字段
头字段名称: Sec-CH-UA-Model
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.8 The 'Sec-CH-UA-Model' Header Field)
9.9. 'Sec-CH-UA-Platform' 头字段
头字段名称: Sec-CH-UA-Platform
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.9 The 'Sec-CH-UA-Platform' Header Field)
9.10. 'Sec-CH-UA-Platform-Version' 头字段
头字段名称: Sec-CH-UA-Platform-Version
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.10 The 'Sec-CH-UA-Platform-Version' Header Field)
9.11. 'Sec-CH-UA-WoW64' 头字段
头字段名称: Sec-CH-UA-WoW64
适用协议: http
状态: 标准
作者/变更控制者: IETF
规范文档: 本规范 (§ 3.11 The 'Sec-CH-UA-WoW64' Header Field)
9.12. 'User-Agent' 头字段
头字段名称: User-Agent
适用协议: http
状态: 已废弃
作者/变更控制者: IETF
规范文档: 本规范 (§ 8.1 The 'User-Agent' Header),以及 [rfc9110] 第 5.5.3 节
10. 致谢
感谢 Aaron Tagliaboschi、Ali Beyad、ArkUmbra、Dustin Mitchell、Erik Anderson、jasonwee、Luke Williams、Mike West、Martin Thomson、Toru Kobayashi 对本规范提出的宝贵意见与贡献。