简介
注意:本文档目前仍处于开发阶段。预计在不久的将来会有所改进。
Google 安全浏览 v5 是在 Google 安全浏览 v4 的基础上发展而来。v5 中涉及的两项主要变更是数据新鲜度和 IP 隐私保护。此外,我们还改进了 API Surface,提高了灵活性、效率并减少膨胀。此外,Google 安全浏览 v5 的设计宗旨是方便您轻松从 v4 进行迁移。
目前,Google 同时提供了 v4 和 v5,并且都被视为可正式投入使用。您可以使用 v4 或 v5。我们尚未公布 v4 的停用日期;我们会至少提前一年发出通知。本页将介绍 v5 以及从 v4 到 v5 的迁移指南;完整的 v4 文档仍然可用。
数据新鲜度
Google 安全浏览 v5 相对于 v4(具体而言是 v4 Update API)的一项重大改进是数据新鲜度和覆盖率。由于保护在很大程度上依赖于客户端维护的本地数据库,因此本地数据库更新的延迟和大小是导致错失保护的主要因素。在 v4 中,典型的客户端需要 20 到 50 分钟才能获取最新版本的威胁列表。遗憾的是,网络钓鱼攻击传播速度很快:截至 2021 年,60% 的网站发起攻击的时间不到 10 分钟。我们的分析表明,约有 25-30% 的钓鱼式攻击防护缺失是因为此类数据过时。此外,有些设备无法管理 Google 安全浏览中的全部威胁列表,该列表还在不断扩大。
在 v5 中,我们引入了一种称为实时保护的操作模式。这样可以避免上述数据过时问题。在 v4 中,客户端需要下载并维护一个本地数据库,针对本地下载的威胁列表执行检查,然后在存在部分前缀匹配时,执行请求下载完整的哈希值。在 v5 中,虽然客户端应继续下载并维护威胁列表的本地数据库,但现在客户端还需要下载可能良性网站的列表(称为全局缓存),对该全局缓存和本地威胁列表执行本地检查,最后当威胁列表存在部分前缀匹配或全局缓存中没有匹配时,执行下载完整哈希的请求。(如需详细了解客户端所需的本地处理,请参阅下文提供的步骤。)这表示从“默认允许”变为“默认检查”,因为威胁在网络上的传播速度更快,从而加强保护。换言之,该协议旨在提供近乎实时的保护:我们的目标是让客户能够从更新鲜的 Google 安全浏览数据中受益。
IP 隐私
在传送请求的过程中,Google 安全浏览(v4 或 v5)不会处理与用户身份相关的任何信息。Cookie (如果发送)会被忽略。这些请求的发起 IP 地址为 Google 所知,但 Google 仅将 IP 地址用于必要的网络需求(即发送响应)以及用于防范 DoS 攻击。
在与 v5 同时推出的同时,我们还引入了一个名为 Safe Browsing Oblivious HTTP Gateway API 的配套 API。这会使用 Oblivious HTTP 来隐藏最终用户的从 Google 获取的 IP 地址。具体过程为:让非相互冲突的第三方处理用户请求的加密版本,然后将其转发给 Google。因此,第三方只能访问 IP 地址,而 Google 只能访问请求的内容。第三方运营 Oblivious HTTP Relay(例如 Fastly 提供的此服务),而 Google 运营 Oblivious HTTP 网关。这是一个可选的配套 API。如果将该功能与 Google 安全浏览功能结合使用,最终用户的不再将 IP 地址发送给 Google。
正确用法
允许的使用情形
Safe Browsing API 只能用于非商业用途(即“不用于销售目的或用于创收目的”)。如果您需要用于商业目的的解决方案,请参阅 Web Risk。
价格
所有 Google Safe Browsing API 都是免费的。
配额
启用 Safe Browsing API 时,系统会为开发者分配默认的用量配额。您可以在 Google Developer Console 中查看当前分配情况和使用情况。如果您预计的用量会超出当前分配的配额,可以通过 Play 管理中心的“配额”界面申请更多配额。我们会审核这些申请,并要求您在申请增加配额时与您联系,以确保我们的服务可用性能够满足所有用户的需求。
适当的网址
Google 安全浏览功能旨在对显示在浏览器地址栏中的网址执行操作。而不适用于检查子资源(例如,HTML 文件引用的 JavaScript 或图片,或由 JavaScript 启动的 WebSocket 网址)。此类子资源网址不应进行 Google 安全浏览检查。
如果访问某个网址会导致重定向(例如 HTTP 301),则 Google 安全浏览功能会对重定向网址进行检查。客户端网址操纵(例如 History.pushState
)不会导致系统根据 Google 安全浏览功能对新网址进行检查。
用户警告
如果您使用 Google 安全浏览功能就特定网页带来的风险向用户发出警告,则应遵循以下指南。
这些指南明确说明了 Google 并非完全确定相应网页是否为不安全的网络资源,相关警告只是指出潜在风险,从而保护您和 Google 免受误解。
- 在向用户显示的警告中,不得让用户相信相关网页毫无疑问是不安全的网络资源。当您提及正在识别的网页或它可能给用户带来的潜在风险时,必须使用诸如“可疑”、“可能”、“可能”、“可能”等字词来限定警告。
- 您的警告必须让用户能够通过查看 Google 对各种威胁的定义来了解详情。建议您访问以下链接: <ph type="x-smartling-placeholder">
- 当您针对安全浏览服务识别为有风险的网页显示警告时,您必须添加“由 Google 提供的建议”这行文字,以注明相应内容属于 Google以及指向安全浏览警告的链接。如果您的商品还会根据其他来源显示警告,则您不得在基于非 Google 数据生成的警告中添加 Google 属性。
您必须在产品文档中提供一条通知,告知用户 Google 安全浏览功能提供的保护并非十全十美。该工具必须让他们知道,系统有可能出现误报(即被标记为存在风险的安全网站)和误报(即未标记有风险的网站)。我们建议您使用以下语言:
Google 致力于提供有关不安全网络资源的最准确和最新信息。不过,Google 无法保证所提供的信息全面且准确无误:有些有风险的网站可能无法被识别出来,而有些安全网站可能会被错误识别。
运营模式
Google 安全浏览 v5 允许客户端从三种操作模式中进行选择。
实时模式
当客户端选择在实时模式下使用 Google 安全浏览 v5 时,客户端将在其本地数据库中维护:(i) 可能良性网站的全局缓存(格式为主机后缀/路径前缀网址表达式的 SHA256 哈希);(ii) 一组威胁列表(格式为主机-后缀/路径前缀网址表达式的 SHA256 哈希前缀)。概括来讲,每当客户端要检查特定网址时,都会使用全局缓存执行本地检查。如果该检查通过,则会执行当地威胁列表检查。否则,客户端会继续进行实时哈希检查,如下所述。
除本地数据库外,客户端还会维护一个本地缓存。此类本地缓存无需存储在永久性存储空间中,并且可以在内存紧张时被清除。
下面提供了该程序的详细规范。
本地列表模式
当客户端选择在此模式下使用 Google 安全浏览 v5 时,客户端行为与 v4 Update API 类似,只不过使用的是经过改进的 v5 API Surface。客户端会在其本地数据库中维护一组威胁列表,这些列表的格式采用主机后缀/路径前缀网址表达式的 SHA256 哈希前缀。每当客户想要检查特定网址时,都会使用本地威胁列表执行检查。当且仅当存在匹配项时,客户端才会连接到服务器以继续检查。
与上述内容一样,客户端还将维护一个不需要位于永久性存储空间中的本地缓存。
无存储实时模式
当客户端选择在无存储实时模式下使用 Google 安全浏览 v5 时,便无需维护任何永久性本地数据库。但是,客户端仍应保留本地缓存。此类本地缓存无需存储在永久性存储空间中,并且可以在内存紧张时被清除。
每当客户端想要检查特定网址时,客户端始终会连接到服务器以执行检查。此模式类似于 v4 Lookup API 的客户端可能会实现的模式。
与实时模式相比,此模式可能会使用更多网络带宽,但如果客户端不方便保持持续的本地状态,该模式可能更合适。
检查网址
本部分包含有关客户端如何检查网址的详细规范。
网址的规范化
在检查任何网址之前,客户端应对该网址执行某种规范化。
首先,我们假设客户端已解析网址并根据 RFC 2396 使其有效。如果网址使用国际化域名 (IDN),则客户端应将网址转换为 ASCII Punycode 表示形式。该网址必须包含路径组成部分;也就是说,域名后必须至少有一个斜杠(而不是 http://google.com/
,而不是 http://google.com
)。
首先,从网址中移除制表符 (0x09)、CR (0x0d) 和 LF (0x0a) 字符。请勿移除这些字符的转义序列(例如 %0a
)。
其次,如果网址以片段结尾,请移除片段。例如,将 http://google.com/#frag
缩短为 http://google.com/
。
第三,反复使用百分号对网址进行反转义,直到其不再转义。(这可能会导致网址无效)。
如需规范化主机名,请执行以下操作:
从网址中提取主机名,然后:
- 移除所有前导和尾随的点。
- 用单点替换连续的点。
- 如果主机名可以解析为 IPv4 地址,请将其标准化为 4 个以点分隔的十进制值。客户端应处理所有合法的 IP 地址编码,包括八进制、十六进制以及少于四个组成部分。
- 如果主机名可以解析为带括号的 IPv6 地址,请将其标准化,具体方法是:移除组成部分中不必要的前导零,并使用双冒号语法将零组成部分收起来。例如,
[2001:0db8:0000::1]
应转换为[2001:db8::1]
。如果主机名是以下两种特殊 IPv6 地址类型之一,请将其转换为 IPv4: <ph type="x-smartling-placeholder">- </ph>
- IPv4 映射的 IPv6 地址(例如
[::ffff:1.2.3.4]
),应转换为1.2.3.4
; - 使用众所周知的前缀 64:ff9b::/96(例如
[64:ff9b::1.2.3.4]
)的 NAT64 地址,应转换为1.2.3.4
。
- IPv4 映射的 IPv6 地址(例如
- 将整个字符串小写。
要对路径进行规范化,请执行以下操作:
- 将
/./
替换为/
,并移除/../
及前面的路径组成部分,从而解析路径中的/../
和/./
序列。 - 将连续斜杠替换成单个斜杠字符。
请勿将这些路径规范化应用于查询参数。
在网址中,对所有 <= ASCII 32、>= 127、#
或 %
的字符进行百分号转义。转义字符应使用大写的十六进制字符。
主机后缀路径前缀表达式
对网址进行规范化后,下一步是创建后缀/前缀表达式。每个后缀/前缀表达式都由主机后缀(或完整主机)和路径前缀(或完整路径)组成。
客户端将形成多达 30 种不同的主机后缀和路径前缀组合。这些组合仅使用网址的主机和路径部分。方案、用户名、密码和端口会被舍弃。如果该网址包含查询参数,则至少有一个组合会包含完整路径和查询参数。
对于主机,客户端最多可尝试 5 个不同的字符串。它们是:
- 如果主机名不是 IPv4 或 IPv6 字面量,则最多可输入 4 个主机名(以 eTLD+1 域名开头,然后添加连续的前导部分)。应根据公共后缀列表确定 eTLD+1。例如,
a.b.example.com
会产生 eTLD+1 域名example.com
,并且该主机具有一个额外的主机组件b.example.com
。 - 网址中的确切主机名。接着前面的示例,系统将检查
a.b.example.com
。
对于路径,客户端最多可尝试 6 个不同的字符串。他们分别是:
- 网址的确切路径包括查询参数。
- 网址的确切路径不包含查询参数。
- 从根 (/) 开始,依次附加路径组成部分(包括尾随斜杠)形成的四个路径。
以下示例演示了检查行为:
对于网址 http://a.b.com/1/2.html?param=1
,客户端将尝试以下可能的字符串:
a.b.com/1/2.html?param=1
a.b.com/1/2.html
a.b.com/
a.b.com/1/
b.com/1/2.html?param=1
b.com/1/2.html
b.com/
b.com/1/
对于网址 http://a.b.c.d.e.f.com/1.html
,客户端将尝试以下可能的字符串:
a.b.c.d.e.f.com/1.html
a.b.c.d.e.f.com/
c.d.e.f.com/1.html
c.d.e.f.com/
d.e.f.com/1.html
d.e.f.com/
e.f.com/1.html
e.f.com/
f.com/1.html
f.com/
(注意:请跳过 b.c.d.e.f.com
,因为我们只考虑最后五个主机名组件以及完整的主机名。)
对于网址 http://1.2.3.4/1/
,客户端将尝试以下可能的字符串:
1.2.3.4/1/
1.2.3.4/
对于网址 http://example.co.uk/1
,客户端将尝试以下可能的字符串:
example.co.uk/1
example.co.uk/
哈希技术
Google 安全浏览功能仅使用 SHA256 作为哈希函数。此哈希函数应应用于上述表达式。
根据具体情况,完整的 32 字节哈希会被截断为 4 个字节、8 个字节或 16 个字节:
目前,在使用 hashes.search 方法时,我们要求将请求中的哈希值截断为正好 4 个字节。在此请求中发送额外的字节会侵犯用户隐私。
使用 hashList.get 方法或 hashLists.batchGet 方法下载本地数据库的列表时,服务器发送的哈希长度受列表性质和客户端偏好的哈希长度影响(通过
desired_hash_length
参数传达)。
实时网址检查程序
当客户端选择实时操作模式时,会用到此过程。
此过程接受单个网址 u
,并返回 SAFE
、UNSAFE
或 UNSURE
。如果它返回 SAFE
,Google 安全浏览功能认为该网址是安全的。如果它返回 UNSAFE
,Google 安全浏览功能认为该网址可能不安全,那么您应采取适当措施:例如向最终用户显示警告、将收到的邮件移至“垃圾邮件”文件夹,或要求用户先额外确认,然后才能继续操作。如果它返回 UNSURE
,则之后应使用以下本地检查程序。
- 让
expressions
成为由网址u
生成的后缀/前缀表达式列表。 - 以
expressionHashes
为一个列表,其中元素是expressions
中每个表达式的 SHA256 哈希值。 - 对于
expressionHashes
的每个hash
: <ph type="x-smartling-placeholder">- </ph>
- 如果可以在全局缓存中找到
hash
,则返回UNSURE
。
- 如果可以在全局缓存中找到
- 让
expressionHashPrefixes
是一个列表,其中元素是expressionHashes
中每个哈希的前 4 个字节。 - 对于
expressionHashPrefixes
的每个expressionHashPrefix
: <ph type="x-smartling-placeholder">- </ph>
- 在本地缓存中查找
expressionHashPrefix
。 - 如果找到缓存条目:
<ph type="x-smartling-placeholder">
- </ph>
- 确定当前时间是否晚于其到期时间。
- 如果结果大于该值:
<ph type="x-smartling-placeholder">
- </ph>
- 从本地缓存中移除找到的缓存条目。
- 继续循环。
- 如果小于或等于:
<ph type="x-smartling-placeholder">
- </ph>
- 从
expressionHashPrefixes
中移除这个特定expressionHashPrefix
。 - 检查是否在缓存条目中找到
expressionHashes
中对应的完整哈希值。 - 如果找到,则返回
UNSAFE
。 - 如果找不到,请继续循环。
- 从
- 如果未找到缓存条目,请继续循环。
- 在本地缓存中查找
- 使用 RPC SearchHashes 或 REST 方法 hashes.search 将
expressionHashPrefixes
发送到 Google 安全浏览 v5 服务器。如果发生错误(包括网络连接错误、HTTP 错误等),则返回UNSURE
。否则,将响应设为从 SB 服务器收到的response
,这是一个完整的哈希值列表以及一些用于识别威胁性质(社会工程学、恶意软件等)的辅助信息,以及缓存到期时间expiration
。 - 对于
response
的每个fullHash
: <ph type="x-smartling-placeholder">- </ph>
- 将
fullHash
与expiration
一起插入本地缓存。
- 将
- 对于
response
的每个fullHash
: <ph type="x-smartling-placeholder">- </ph>
- 让
isFound
作为在expressionHashes
中查找fullHash
的结果。 - 如果
isFound
为 False,则继续执行循环。 - 如果
isFound
为 True,则返回UNSAFE
。
- 让
- 返回
SAFE
。
虽然此协议指定了客户端何时向服务器发送 expressionHashPrefixes
,但协议并没有明确指定具体的发送方式。例如,客户端可以在单个请求中发送所有 expressionHashPrefixes
,也可以通过单独的请求将 expressionHashPrefixes
中的每个前缀发送到服务器(可能会并行继续)。客户端也可以将不相关或随机生成的哈希前缀与 expressionHashPrefixes
中的哈希前缀一起发送,前提是单个请求中发送的哈希前缀数不超过 30。
LocalThreat 列表网址检查过程
当客户端选择本地列表操作模式时,将使用此过程。当上述 RealTimeCheck 过程的客户端返回 UNSURE
的值时,也会使用它。
此过程接受单个网址 u
并返回 SAFE
或 UNSAFE
。
- 让
expressions
成为由网址u
生成的后缀/前缀表达式列表。 - 以
expressionHashes
为一个列表,其中元素是expressions
中每个表达式的 SHA256 哈希值。 - 让
expressionHashPrefixes
是一个列表,其中元素是expressionHashes
中每个哈希的前 4 个字节。 - 对于
expressionHashPrefixes
的每个expressionHashPrefix
: <ph type="x-smartling-placeholder">- </ph>
- 在本地缓存中查找
expressionHashPrefix
。 - 如果找到缓存条目:
<ph type="x-smartling-placeholder">
- </ph>
- 确定当前时间是否晚于其到期时间。
- 如果结果大于该值:
<ph type="x-smartling-placeholder">
- </ph>
- 从本地缓存中移除找到的缓存条目。
- 继续循环。
- 如果小于或等于:
<ph type="x-smartling-placeholder">
- </ph>
- 从
expressionHashPrefixes
中移除这个特定expressionHashPrefix
。 - 检查是否在缓存条目中找到
expressionHashes
中对应的完整哈希值。 - 如果找到,则返回
UNSAFE
。 - 如果找不到,请继续循环。
- 从
- 如果未找到缓存条目,请继续循环。
- 在本地缓存中查找
- 对于
expressionHashPrefixes
的每个expressionHashPrefix
: <ph type="x-smartling-placeholder">- </ph>
- 在本地威胁列表数据库中查找
expressionHashPrefix
。 - 如果在本地威胁列表数据库中找不到
expressionHashPrefix
,请将其从expressionHashPrefixes
中移除。
- 在本地威胁列表数据库中查找
- 使用 RPC SearchHashes 或 REST 方法 hashes.search 将
expressionHashPrefixes
发送到 Google 安全浏览 v5 服务器。如果发生错误(包括网络连接错误、HTTP 错误等),则返回SAFE
。否则,将响应设为从 SB 服务器收到的response
,这是一个完整的哈希值列表以及一些用于识别威胁性质(社会工程学、恶意软件等)的辅助信息,以及缓存到期时间expiration
。 - 对于
response
的每个fullHash
: <ph type="x-smartling-placeholder">- </ph>
- 将
fullHash
与expiration
一起插入本地缓存。
- 将
- 对于
response
的每个fullHash
: <ph type="x-smartling-placeholder">- </ph>
- 让
isFound
作为在expressionHashes
中查找fullHash
的结果。 - 如果
isFound
为 False,则继续执行循环。 - 如果
isFound
为 True,则返回UNSAFE
。
- 让
- 返回
SAFE
。
无本地数据库时的实时网址检查过程
当客户端选择无存储实时操作模式时,就会执行此过程。
此过程接受单个网址 u
并返回 SAFE
或 UNSAFE
。
- 让
expressions
成为由网址u
生成的后缀/前缀表达式列表。 - 以
expressionHashes
为一个列表,其中元素是expressions
中每个表达式的 SHA256 哈希值。 - 让
expressionHashPrefixes
是一个列表,其中元素是expressionHashes
中每个哈希的前 4 个字节。 - 对于
expressionHashPrefixes
的每个expressionHashPrefix
: <ph type="x-smartling-placeholder">- </ph>
- 在本地缓存中查找
expressionHashPrefix
。 - 如果找到缓存条目:
<ph type="x-smartling-placeholder">
- </ph>
- 确定当前时间是否晚于其到期时间。
- 如果结果大于该值:
<ph type="x-smartling-placeholder">
- </ph>
- 从本地缓存中移除找到的缓存条目。
- 继续循环。
- 如果小于或等于:
<ph type="x-smartling-placeholder">
- </ph>
- 从
expressionHashPrefixes
中移除这个特定expressionHashPrefix
。 - 检查是否在缓存条目中找到
expressionHashes
中对应的完整哈希值。 - 如果找到,则返回
UNSAFE
。 - 如果找不到,请继续循环。
- 从
- 如果未找到缓存条目,请继续循环。
- 在本地缓存中查找
- 使用 RPC SearchHashes 或 REST 方法 hashes.search 将
expressionHashPrefixes
发送到 Google 安全浏览 v5 服务器。如果发生错误(包括网络连接错误、HTTP 错误等),则返回SAFE
。否则,将响应设为从 SB 服务器收到的response
,这是一个完整的哈希值列表以及一些用于识别威胁性质(社会工程学、恶意软件等)的辅助信息,以及缓存到期时间expiration
。 - 对于
response
的每个fullHash
: <ph type="x-smartling-placeholder">- </ph>
- 将
fullHash
与expiration
一起插入本地缓存。
- 将
- 对于
response
的每个fullHash
: <ph type="x-smartling-placeholder">- </ph>
- 让
isFound
作为在expressionHashes
中查找fullHash
的结果。 - 如果
isFound
为 False,则继续执行循环。 - 如果
isFound
为 True,则返回UNSAFE
。
- 让
- 返回
SAFE
。
与实时网址检查过程一样,此过程并未指定向服务器发送哈希前缀的确切方式。例如,客户端可以在单个请求中发送所有 expressionHashPrefixes
,也可以通过单独的请求将 expressionHashPrefixes
中的每个前缀发送到服务器(可能会并行继续)。客户端也可以将不相关或随机生成的哈希前缀与 expressionHashPrefixes
中的哈希前缀一起发送,前提是单个请求中发送的哈希前缀数不超过 30。
本地数据库维护
除非客户端选择无存储实时模式,否则 Google 安全浏览 v5 会要求客户端维护本地数据库。此本地数据库的格式和存储由客户端决定。从概念上讲,此本地数据库的内容可以视为包含各种列表文件的文件夹,这些文件的内容是 SHA256 哈希或哈希前缀。
数据库更新
客户端会定期调用 hashList.get 方法或 hashLists.batchGet 方法来更新数据库。由于典型的客户端希望一次更新多个列表,因此建议使用 hashLists.batchGet 方法。
列表由不同的名称进行标识。名称是长度为几个字符的短 ASCII 字符串。
与 V4 不同,在 V4 中,列表通过威胁类型、平台类型、威胁条目类型的元组来标识,而 V5 中的列表只是通过名称来标识。当多个 v5 名单可能具有相同威胁类型时,这可提供灵活性。v5 中移除了平台类型和威胁条目类型。
为列表选择名称后,便无法再重命名。此外,列表显示后,就永远不会删除(如果列表不再有用,则它会变为空的,但会继续存在)。因此,可在 Google 安全浏览客户端代码中对这些名称进行硬编码。
hashList.get 方法和 hashLists.batchGet 方法都支持增量更新。使用增量更新可以节省带宽并提升性能。增量更新的工作原理是:在客户端的列表版本和列表的最新版本之间提供增量。(如果客户端是新部署的,且没有任何可用版本,则可用完整更新。)增量更新包含移除索引和添加内容。客户端首先应从其本地数据库中移除指定索引处的条目,然后再应用添加的操作。
最后,为防止损坏,客户端应对照服务器提供的校验和检查存储的数据。只要校验和不匹配,客户端就应执行完全更新。
对列表内容进行解码
解码哈希和哈希前缀
所有列表都使用特殊编码来缩减大小。这种编码的工作原理是识别 Google 安全浏览列表在概念上包含一组从统计上无法区分的哈希或哈希前缀。如果我们对这些整数进行排序并取它们相邻的差值,则此类相邻差值预计为“小”然后,Golomb-Rice 编码就利用了这种小规模。
假设三个主机后缀路径前缀表达式(a.example.com/
、b.example.com/
和 y.example.com/
)将使用 4 字节哈希前缀进行传输。再假设 Rice 参数(以 k 表示)设为 30。服务器会先计算这些字符串的完整哈希值,分别为:
291bc5421f1cd54d99afcc55d166e2b9fe42447025895bf09dd41b2110a687dc a.example.com/
1d32c5084a360e58f1b87109637a6810acad97a861a7769e8f1841410d2a960c b.example.com/
f7a502e56e8b01c6dc242b35122683c9d25d07fb1f532d9853eb0ef3ff334f03 y.example.com/
然后,服务器会为上述内容分别生成 4 字节哈希前缀,即 32 字节完整哈希的前 4 个字节,将其解释为 big-endian 32 位整数。大字节序是指完整哈希的第一个字节成为 32 位整数的最高有效字节。此步骤将生成整数 0x291bc542、0x1d32c508 和 0xf7a502e5。
服务器必须按字典顺序对这三个哈希前缀进行排序(等同于大端序中的数字排序),排序结果为 0x1d32c508、0x291bc542、0xf7a502e5。第一个哈希前缀原封不动地存储在 first_value
字段中。
然后,服务器会计算两个相邻的差异,分别为 0xbe9003a 和 0xce893da3。若将 k 设为 30,服务器会将这两个数字拆分为长度为 2 位和 30 位的商和余数部分。对于第一个数,商部分为零,余数为 0xbe9003a;对于第二个数,商部分为 3,因为最高有效两位的二进制数为 11,余数为 0xe893da3。对于给定商 q
,它正好使用 1 + q
位编码为 (1 << q) - 1
;余数则直接使用 k 位进行编码。第一个数的商部分编码为 0,余数部分编码为二进制数 001011111010010000000000111010;第二个数的商部分编码为 0111,余数部分编码为 001110100010010011110110100011。
当这些数字形成一个字节字符串时,将使用小端字节序。从概念上讲,可能更容易想象从最低有效位开始形成一个长位串:我们取第一个数字的商部分,然后在第一个数字的剩余部分前面加上商;再加上第二个数的商部分,再加上余数部分。这应该会产生以下大量数字(为清晰起见,添加了换行符和注释):
001110100010010011110110100011 # Second number, remainder part
0111 # Second number, quotient part
001011111010010000000000111010 # First number, remainder part
0 # First number, quotient part
一行写成,代码如下:
00111010001001001111011010001101110010111110100100000000001110100
显然,这个数字远远超过了单个字节可用的 8 位。然后,小端序编码会获取该数字中的最低有效 8 位,并将其输出为第一个字节,即 01110100。为清楚起见,我们可以将上述位串分成 8 组(从最低有效位开始):
0 01110100 01001001 11101101 00011011 10010111 11010010 00000000 01110100
然后,小端字节序编码从右侧获取每个字节,并将其放入字节串中:
01110100
00000000
11010010
10010111
00011011
11101101
01001001
01110100
00000000
可以看出,由于我们从概念上将新部分添加到左侧大量数字(即添加更多有效位),但从右侧进行编码(即最低有效位),因此编码和解码可以增量执行。
这最终会产生
additions_four_bytes {
first_value: 489866504
rice_parameter: 30
entries_count: 2
encoded_data: "t\000\322\227\033\355It\000"
}
客户端只需反向执行上述步骤,即可对哈希前缀进行解码。与 v4 不同,由于哈希前缀整数会被解释为大端序,因此在最后无需执行字节交换。
解码移除索引
删除索引采用与上述完全相同的技术进行编码,且使用 32 位整数。删除索引的编码和解码在 v4 与 v5 之间没有变化。
可用列表
建议在 v5alpha1 中使用以下列表:
列表名称 | 对应的 v4 ThreatType 枚举 |
说明 |
---|---|---|
gc |
收不到任何通知 | 此列表为全局缓存列表。这是一个只有在实时操作模式中使用的特殊列表。 |
se |
SOCIAL_ENGINEERING |
此列表包含 SOCIAL_ENGINEERING 威胁类型的威胁。 |
mw |
MALWARE |
此列表包含桌面平台的恶意软件威胁类型。 |
uws |
UNWANTED_SOFTWARE |
此列表包含桌面平台的 UNWANTED_SOFTWARE 威胁类型。 |
uwsa |
UNWANTED_SOFTWARE |
此列表包含 Android 平台的 UNWANTED_SOFTWARE 威胁类型。 |
pha |
POTENTIALLY_HARMFUL_APPLICATION |
此列表包含 Android 平台的 POTENTIALLY_HARMFUL_APPLICATION 威胁类型的威胁。 |
我们日后会提供更多名单,届时上述表格将会扩充。
客户端可以运行缓存代理服务器来检索上述部分或全部列表,然后让客户端与代理服务器联系。若要实现此方法,我们建议使用较短的缓存时长,如 5 分钟;但将来可能会使用标准的 Cache-Control
HTTP 标头来传达此缓存持续时间。
更新频率
客户端应检查 minimum_wait_duration
字段中的服务器返回值,并使用该值来安排数据库的下一次更新。此值可能为零(minimum_wait_duration
字段完全缺失),在这种情况下,客户端应立即再次执行更新。
示例请求
本部分记录了一些直接使用 HTTP API 访问 Google 安全浏览功能的示例。通常建议使用生成的语言绑定,因为它能以方便的方式自动处理编码和解码。请参阅关于该绑定的文档。
下面是一个使用 hashes.search 方法的 HTTP 请求示例:
GET https://safebrowsing.googleapis.com/v5/hashes:search?key=INSERT_YOUR_API_KEY_HERE&hashPrefixes=WwuJdQ
响应正文是采用协议缓冲区格式的载荷,您随后可以对其进行解码。
以下是使用 hashLists.batchGet 方法的 HTTP 请求示例:
GET https://safebrowsing.googleapis.com/v5alpha1/hashLists:batchGet?key=INSERT_YOUR_API_KEY_HERE&names=se&names=mw
响应正文同样是协议缓冲区格式的负载,您可以对其进行解码。
迁移指南
如果您目前使用的是 v4 Update API,则可以从 v4 无缝迁移到 v5,而无需重置或清空本地数据库。本部分介绍了如何执行此操作。
转化名单更新
在 v4 中,可以使用 threatListUpdates.fetch 方法下载列表。在 v5 中,可以切换到 hashLists.batchGet 方法。
应对请求进行以下更改:
- 完全移除 v4
ClientInfo
对象。您无需通过专用字段提供客户端标识,只需使用广为人知的用户代理标头即可。虽然在此标头中并没有规定提供客户标识的格式,但我们建议您直接添加原始客户 ID 和客户版本(用空格字符或斜线字符分隔)。 - 对于每个 v4
ListUpdateRequest
对象:- 在上表中查找对应的 v5 列表名称,并在 v5 请求中提供该名称。
- 移除
threat_entry_type
或platform_type
等不需要的字段。 - v4 中的
state
字段与 v5versions
字段直接兼容。在 v4 中使用state
字段发送到服务器的相同字节字符串在 v5 中只需使用versions
字段即可发送。 - 对于 v4 限制条件,v5 使用名为
SizeConstraints
的简化版本。应舍弃其他字段,例如region
。
应对响应进行以下更改:
- v4 枚举
ResponseType
直接替换为名为partial_update
的布尔值字段。 minimum_wait_duration
字段现在可为零或省略。如果是,则请求客户端立即发出另一个请求。只有当客户端在SizeConstraints
中为最大更新大小指定小于数据库大小上限的限制时,才会发生这种情况。- 32 位整数的 Rice 解码算法需要调整。不同之处在于编码数据编码为不同的字节序。在 v4 和 v5 中,32 位哈希前缀都按字典顺序排序。但在 v4 中,这些前缀在排序时被视为小端序,而在 v5 中,这些前缀在排序时被视为大端序。这意味着客户端无需进行任何排序,因为字典排序与采用大端序的数字排序相同。您可以在此处找到 Chromium v4 实现的此类示例。您可以删除此类排序。
- 需要针对其他哈希长度实现大米解码算法。
转换哈希搜索
在 v4 中,可以使用 fullHashes.find 方法获取完整哈希值。在 v5 中,等效方法是 hashes.search 方法。
应对请求进行以下更改:
- 设计代码结构,使其仅发送长度正好 4 个字节的哈希前缀。
- 完全移除 v4
ClientInfo
对象。您无需通过专用字段提供客户端标识,只需使用广为人知的用户代理标头即可。虽然在此标头中并没有规定提供客户标识的格式,但我们建议您直接添加原始客户 ID 和客户版本(用空格字符或斜线字符分隔)。 - 移除
client_states
字段。不再需要此函数。 - 不再需要包含
threat_types
和类似字段。
应对响应进行以下更改:
minimum_wait_duration
字段已移除。客户端可以随时根据需要发出新的请求。- v4
ThreatMatch
对象已简化为FullHash
对象。 - 缓存已简化为单个缓存时长。请参阅上述与缓存交互的过程。