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)
用户代理应保持这些字符串简短直接,但服务器必须接受任意值,因为这些值均由用户代理自由构造。
-
x86 CPU 架构 => "x86"
-
ARM CPU 架构 => "arm"
其他 CPU 架构可视情况映射到上述值之一,或者映射为空字符串。
用户代理应返回平台架构或平台位宽的空字符串或虚构值,除非用户平台同时满足以下两个条件:
-
很可能需要下载二进制可执行文件。
-
不同 CPU 架构很可能需要不同的二进制资源,并且这些资源可能可用。
用户代理在移动性为 false 时,必须为型号返回空字符串。即使移动性为 true,型号也必须返回空字符串,除非该平台通常会暴露型号信息。
用户代理可为类型为 sf-string
的提示返回空字符串,为类型为 sf-boolean
的提示返回 false
,或者为以下提示返回任意虚构值(为隐私、兼容性或其它原因):完整版本、平台架构、平台位宽、wow64 属性或型号。
3.1. 'Sec-CH-UA' 头字段
Sec-CH-UA
请求头字段为服务器提供用户代理的品牌信息和重要版本信息。它是一个结构化头,其值必须为列表 [rfc9651]。
列表项必须为字符串。每项的值应包含 "v" 参数,表示用户代理的版本。
该头的 ABNF 如下:
Sec-CH-UA = sf-list
要返回请求的 Sec-CH-UA
值,执行以下步骤:
注意: 与多数客户端提示不同,由于其包含在低熵提示表中,Sec-CH-UA
头会默认发送,无论服务器是否通过
Accept-CH
头主动选择接收(但仍可由策略控制的客户端提示功能控制)。它被认为是低熵提示,因为只包含用户代理的品牌信息和重要版本号(这些信息可通过分析其它头结构和测试特性支持轻易推断 [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
请求头字段为服务器提供特定平台位宽的信息,即特定用户代理所运行平台架构的位宽。它是一个结构化头,其值必须为字符串 [rfc9651]。
该头的 ABNF 如下:
Sec-CH-UA-Bitness = sf-string
3.4. 'Sec-CH-UA-Form-Factors' 头字段
Sec-CH-UA-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
请求头字段为服务器提供品牌列表中每个品牌的完整版本信息。它是一个结构化头,其值必须为列表 [rfc9651]。
该头的 ABNF 如下:
Sec-CH-UA-Full-Version-List = sf-list
要返回请求的
Sec-CH-UA-Full-Version-List
值,执行以下步骤:
3.7. 'Sec-CH-UA-Mobile' 头字段
Sec-CH-UA-Mobile
请求头字段为服务器提供特定用户代理是否偏好“移动”体验的信息。它是一个结构化头,其值必须为布尔值 [rfc9651]。
该头的 ABNF 如下:
Sec-CH-UA-Mobile = sf-boolean
注意: 类似上面的 Sec-CH-UA
,由于其包含在低熵提示表中,Sec-CH-UA-Mobile
头会默认发送,无论服务器是否通过
Accept-CH
主动选择接收(但仍可由策略控制的客户端提示功能控制)。它被视为低熵提示,因为它只是一位信息且可由用户直接控制。
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
注意: 类似上面的 Sec-CH-UA
,由于其包含在低熵提示表中,Sec-CH-UA-Platform
头会默认发送,无论服务器是否通过
Accept-CH
主动选择接收(但仍可由策略控制的客户端提示功能控制)。
3.10. 'Sec-CH-UA-Platform-Version' 头字段
Sec-CH-UA-Platform-Version
请求头字段为服务器提供特定平台版本的信息,即特定用户代理所运行的平台版本。它是一个结构化头,其值必须为字符串[rfc9651]。
其值为用平台品牌运行获取平台版本的结果。
要获取平台版本,给定字符串 platform,执行以下步骤:
-
如果 platform 为 "Linux":
-
返回空字符串。
-
-
如果 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
属性。 -
追加 macOSVersion 的
majorVersion
、minorVersion
和patchVersion
组件(按顺序)到 platformVersionComponentList。
-
-
如果 platform 为其它值:
-
返回用 U+002E 句点(
.
)分隔 platformVersionComponentList 的连接结果。
要获取旧版 Windows 版本号,执行以下步骤:
-
令 major 为 Win32
GetVersionEx
API 返回的OSVERSIONINFO
的dwMajorVersion
成员值。 -
令 minor 为 Win32
GetVersionEx
API 返回的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 句点字符(
.
)严格分割 input 得到的列表: -
当 index 小于 3 时:
-
返回用 U+002E 句点(
.
)分隔 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
注意: 这些客户端提示可通过以下客户端提示令牌激活: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 fullVersionList
uaFullVersion 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 ;
注意: 用户代理的高熵部分信息通过 Promise
检索,以便让用户代理有机会在暴露前执行可能耗时的检查(例如请求用户授权)。
4.1. 处理模型
4.1.1. WindowOrWorkerGlobalScope
每个用户代理都关联一个品牌列表,该列表通过用重要版本运行创建品牌获得,是一个列表。
每个 WindowOrWorkerGlobalScope
对象都关联一个品牌冻结数组,即
FrozenArray<NavigatorUABrandVersion>
。
初始值为用用户代理的品牌列表运行创建冻结数组的结果。
此外,每个 WindowOrWorkerGlobalScope
对象还关联一个完整版本列表冻结数组,即
FrozenArray<NavigatorUABrandVersion>
。
其值为用完整版本运行创建品牌并
创建冻结数组的结果。
4.1.2. 创建品牌
要求用 version type 创建品牌时,执行以下步骤:
-
令 list 为一个列表。
-
断言 version type 必须为 "完整版本" 或 "重要版本"。
-
用户代理应执行以下步骤:
-
追加一个额外的项到 list,该项为一个
NavigatorUABrandVersion
字典,brand
设为任意品牌,version
设为用 version type 运行创建任意版本的结果。 -
随机化 list 中项的顺序。
注意: 为最小化生成这些随机组件时的缓存差异,可在构建时确定其值,并在用户代理的整个重要版本生命周期内保持不变。
注意: 详见 § 7.2 类 GREASE UA 品牌列表,了解何时及为何应采用这些随机化步骤。
-
-
返回 list。
等价类表示一组被认为彼此兼容的浏览器。例如,共享渲染引擎可以形成一个等价类。
4.1.3. 创建任意品牌和版本值
要创建任意品牌,用户代理必须执行以下步骤:
-
令 arbitraryBrand 为一个仅由ASCII 字母和 0x20 (空格) 组成的字符串。arbitraryBrand 必须包含一个或多个 0x20 (空格) 字节,且长度不超过 20 个ASCII 字节,且不能以 0x20 (空格) 开头或结尾。
-
令 arbitraryBrandList 为用ASCII 空白分割arbitraryBrand后得到的列表。
-
令 greaseyStack 为一个栈。
-
令 greaseyChars 为如下列表:ASCII 字节 « 0x20 (空格)、0x28 (左括号)、0x29 (右括号)、0x2D (-)、0x2E (.)、0x2F (/)、0x3A (:)、0x3B (;)、0x3D (=)、0x3F (?)、0x5F (_) »。
-
令 index 为 0。
-
当 index 小于 arbitraryBrandList的长度减一时:
-
令 greaseyBrandList 为一个列表,同时index设为0。
-
当 greaseyStack非空时:
-
追加 arbitraryBrandList[index] 到 greaseyBrandList。
-
返回用去除首尾 ASCII 空白后的greaseyBrandList(无分隔符)连接结果。
此外,虽然结构化头允许字符串中使用转义的 0x22 (\") 和 0x5C (\\),但这些字符也会导致防火墙兼容性问题。
要创建任意版本,给定version type,执行以下步骤:
注意: 用户代理可以选择发送任意低版本号以确保版本检测正常,并应定期变化。
4.1.4. 创建品牌-版本列表
要创建品牌-版本列表,给定brands和version type,执行以下步骤:
4.1.5. Getter方法
获取brands
属性时,必须返回this的相关全局对象的品牌冻结数组。
4.1.6. getHighEntropyValues
方法
getHighEntropyValues(hints)
方法必须执行以下步骤:
-
令 uaData 为新建的
UADataValues
。 -
如果this的相关全局对象的关联文档不被允许使用ch-ua-high-entropy-values功能,则用uaData解析p。
-
否则,以下步骤并行执行:
-
如果 hints 包含 "architecture",则设置 uaData["
architecture
"] 为用户代理的平台架构。 -
如果 hints 包含 "formFactors",则设置 uaData["
formFactors
"] 为用户代理的形态因子。 -
如果 hints 包含 "fullVersionList",则设置 uaData["
fullVersionList
"] 为 this的相关全局对象的完整版本列表冻结数组。 -
如果 hints 包含 "platformVersion",则设置 uaData["
platformVersion
"] 为用平台品牌运行获取平台版本的结果。 -
如果 hints 包含 "uaFullVersion",则设置 uaData["
uaFullVersion
"]。
-
-
返回 p。
4.1.7. toJSON
方法
toJSON()
方法必须执行以下步骤:
-
令 uaLowEntropyData 为新建的
UALowEntropyJSON
-
返回 uaLowEntropyData
5. 权限策略集成
本规范定义了由字符串"ch-ua-high-entropy-values
"标识的策略控制功能,其默认允许列表为'*'
。这决定了某文档是否允许通过getHighEntropyValues()
API返回高熵客户端提示值。
注意:
若某文档不允许使用"ch-ua-high-entropy-values"
功能,则getHighEntropyValues()
API仍会为方便起见返回低熵值。
6. 安全与隐私注意事项
6.1. 安全传输
客户端提示不会发送给非安全端点(详见[RFC8942]第2.2.1节中的安全传输要求)。这意味着用户代理信息不会通过明文通道泄露,从而减少了网络攻击者长期构建指定代理行为画像的机会。
6.2. 委托
客户端提示将通过权限策略([permissions-policy-1])从顶级页面委托。这减少了用户代理信息随子资源请求一同发送的概率,从而降低了被动指纹识别的可能性。
此委托由追加客户端提示到请求定义。
6.3. 指纹识别
User Agent Client Hints 的主要目标是减少通过 User-Agent 头字段默认暴露给整个 Web 的熵量,这些信息可用于被动指纹识别。
用户代理可以自主决定提供哪些提示,原则上不应提供超出未裁剪 User-Agent 头字段包含的信息。用户代理可为不想提供的值返回空字符串,或直接拒绝返回某提示。
但仍可能有部分或全部提示被请求并用于第一方或被委托第三方的主动指纹识别。如§ 6.4 访问限制所述,用户代理应考虑针对已知会主动指纹识别用户的方针实施访问限制。
用户代理应注意不要通过 GREASE 类品牌列表引入仅针对单个或极少用户独特的指纹向量。而应采用一种让任意品牌在众多用户间共享的策略(如:在所有用户的主版本间保持稳定)。
6.4. 访问限制
上述客户端提示揭示了大量关于用户代理及其运行设备的信息。用户代理应在授予访问这些信息前谨慎评估,并可在安全传输和委托要求之外施加额外限制。例如,用户代理可选择仅在打算下载时暴露平台架构或平台位宽,以便服务器能提供合适的二进制文件。同样,也可让用户自行控制暴露给服务器的信息,或经由权限提示或设置界面进行访问授权。
7. 实现注意事项
7.1. 'User-Agent' 头字段
用户代理应通过减少User-Agent
头的信息粒度,弃用其使用,转而采用本文档描述的客户端提示模型。该头短期内不太可能完全移除,因为现有网站的内容协商代码仍需其存在(参见[Rossi2015],了解新浏览器在此领域的最新挑战)。
一种可取的做法是让每个用户代理锁定其User-Agent
头值,通过长期保留“like
Gecko”和“AppleWebKit/537.36”等声明确保向后兼容。此策略可逐步推进,先冻结版本号,再将平台和型号信息转为更通用内容,以减少该头带来的指纹。
7.2. 类似 GREASE 的 UA 品牌列表
历史表明,用户代理存在通过谎报品牌来应对网站嗅探脚本的动力,以防止用户因 UA 白/黑名单而被屏蔽。
重置预期在短期内有助于防止品牌列表被滥用,但长期来看效果有限。网络协议领域引入了 GREASE(Generate Random Extensions And Sustain Extensibility)概念 [I-D.ietf-tls-grease],我们可借鉴其应对方案。
用户代理的品牌包含多于一个条目时,可促进品牌列表的标准化处理。通过随机添加额外的、故意错误的、逗号分隔的条目并任意排序,可降低我们对少数必需字符串的依赖。
下面举几个例子:
-
为避免网站将未知浏览器排除在白名单外,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"
-
用户代理必须在品牌中包含多于一个值,其中一个条目必须为任意值。
品牌中的值顺序必须随时间变化,以防接收方依赖固定位置的特定值。
选择 GREASE 策略时,用户代理应关注缓存差异和分析用例,最大限度减少同版本用户代理的差异。
注意: 最小化缓存和分析差异的一种方法是,在构建时确定 UA 集的 GREASE 部分,并在整个主版本生命周期内保持一致。
7.3. 'Sec-CH-' 前缀
限制用户态 JavaScript 代码影响和修改 UA-CH 头具有多种安全优势。同时,目前看不到任何需要这种用户态重写的实际用例。
因此,基于与 TAG 的讨论,合理做法是禁止通过 JavaScript(如
fetch
或 Service Worker)写入这些头,将其标记为浏览器控制的客户端提示,以便文档化和在请求中包含而不会触发 CORS 预检。
故本规范定义的请求头均包含 Sec-CH-
前缀。
8. 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
头字段的使用。
8.1. 'Sec-CH-UA' 头字段
头字段名称:Sec-CH-UA
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.1 'Sec-CH-UA' 头字段)
8.2. 'Sec-CH-UA-Arch' 头字段
头字段名称:Sec-CH-UA-Arch
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.2 'Sec-CH-UA-Arch' 头字段)
8.3. 'Sec-CH-UA-Bitness' 头字段
头字段名称:Sec-CH-UA-Bitness
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.3 'Sec-CH-UA-Bitness' 头字段)
8.4. 'Sec-CH-UA-Form-Factors' 头字段
头字段名称:Sec-CH-UA-Form-Factors
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.4 'Sec-CH-UA-Form-Factors' 头字段)
8.5. 'Sec-CH-UA-Full-Version' 头字段
头字段名称:Sec-CH-UA-Full-Version
适用协议:http
状态:已废弃
作者/变更控制方:IETF
规范文档:本规范(§ 3.5 'Sec-CH-UA-Full-Version' 头字段)
8.6. 'Sec-CH-UA-Full-Version-List' 头字段
头字段名称:Sec-CH-UA-Full-Version-List
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.6 'Sec-CH-UA-Full-Version-List' 头字段)
8.7. 'Sec-CH-UA-Mobile' 头字段
头字段名称:Sec-CH-UA-Mobile
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.7 'Sec-CH-UA-Mobile' 头字段)
8.8. 'Sec-CH-UA-Model' 头字段
头字段名称:Sec-CH-UA-Model
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.8 'Sec-CH-UA-Model' 头字段)
8.9. 'Sec-CH-UA-Platform' 头字段
头字段名称:Sec-CH-UA-Platform
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.9 'Sec-CH-UA-Platform' 头字段)
8.10. 'Sec-CH-UA-Platform-Version' 头字段
头字段名称:Sec-CH-UA-Platform-Version
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.10 'Sec-CH-UA-Platform-Version' 头字段)
8.11. 'Sec-CH-UA-WoW64' 头字段
头字段名称:Sec-CH-UA-WoW64
适用协议:http
状态:标准
作者/变更控制方:IETF
规范文档:本规范(§ 3.11 'Sec-CH-UA-WoW64' 头字段)
8.12. 'User-Agent' 头字段
头字段名称:User-Agent
适用协议:http
状态:已废弃
作者/变更控制方:IETF
规范文档:本规范(§ 7.1 'User-Agent' 头字段),以及 [rfc9110] 第 5.5.3 节
9. 致谢
感谢 Aaron Tagliaboschi、Ali Beyad、ArkUmbra、Dustin Mitchell、Erik Anderson、jasonwee、Luke Williams、Mike West、Martin Thomson 和 Toru Kobayashi 对本规范的宝贵反馈和贡献。