Overview

安全浏览 Oblivious HTTP Gateway API

请注意:此文档目前仍处于开发阶段。预计不久后会有改进。

Safe Browsing Oblivious HTTP Gateway API 是一个基于 IETF RFC 协议(名为 Oblivious HTTP (RFC 9458))构建的可保护隐私的 API。

概览

安全浏览 Oblivious HTTP Gateway API 是一项 Google 服务,可让客户端应用根据 Google 持续更新的不安全网络资源列表来检查网址,同时还会采取额外的隐私保护措施。

这是通过名为 Oblivious HTTP(简称 OHTTP)的轻量级协议实现的。这是一种无状态协议,安全浏览客户端可以使用该协议访问 Google 安全浏览 V5 API,从而在不侵犯用户隐私的情况下获得强大的保护并扩大覆盖范围。

注意:您无法通过此服务访问 Google 安全浏览 V4 API

安全浏览 Oblivious HTTP 协议

RFC 协议

Oblivious HTTP 是在 RFC 9458 中定义的轻量级协议,用于加密 HTTP 消息并将其从客户端发送到目标服务器。这会使用可信中继服务,减少目标服务器使用元数据(例如 IP 地址和连接信息)来识别客户端的行为,从而基于纯 HTTP/S 协议提供隐私和安全保护。该协议使用 RFC 9292 中定义的二进制 HTTP 对 HTTP 请求/响应进行编码/解码。

概括来讲,中继是代理客户端流量的客户端和网关资源之间的连接,通过移除所有客户端标识符(包括 IP 地址等隐私敏感属性),有效地对发送到网关服务的传入 HTTP 请求进行匿名化处理。OHTTP 的额外优势是所有请求都经过端到端加密,这意味着客户端的安全浏览查询(即网址表达式的截断哈希值)对 Relay 不可见。如需查看 Chrome 中的实现示例,请参阅这篇博文

服务的整体架构。
:OHTTP 流程。

客户端可以选择任何中继提供商(例如Fastly)才能与该服务集成。Relay 必须使用 Oauth 2.0 身份验证和以下授权范围才能访问服务。


// OAuth Authorization scope: https://www.googleapis.com/auth/3p-relay-safe-browsing
API 端点
OHTTP 公钥

此端点将提供 RFC 9458 中指定的 OHTTP 公钥配置,客户端将使用配置来加密 OHTTP 请求。


GET https://safebrowsingohttpgateway.googleapis.com/v1/ohttp/hpkekeyconfig?key=<API key>

上述 API 密钥并非绝对必要;服务器不会根据所提供的 API 密钥更改 OHTTP 公钥。客户端可以使用不同的有效 API 密钥访问此端点,或完全不使用 API 密钥,并检查响应是否确实包含相同的 OHTTP 公钥,来探测这一事实。不过,为了便于调试,建议使用 API 密钥;这样,客户端就可以在 Google Cloud 控制台上查看请求数量等统计信息。如果客户端打算提供 API 密钥,请参阅此文档,了解如何设置 API 密钥。

隐私权建议部分所述,为了实现密钥一致性目标,建议客户端供应商设置一个集中式密钥分发基础架构,以便从此端点提取密钥,随后将其分发到其客户端应用。

根据密钥管理指南,在服务器上定期轮替密钥。客户端应经常刷新密钥,即提取和更新密钥的本地副本,以避免解密失败。

客户端应每天刷新(提取和更新)公钥一次。如果使用的是集中式分发机制,该机制应确保每天提取和分发一次密钥。

OHTTP 封装请求

此端点将执行请求解密,以处理 POST 请求的 HTTP 正文中包含的 OHTTP 请求,随后对要在 HTTP 响应中转发回 Relay 的 OHTTP 响应进行加密。客户端必须在 HTTP POST 请求中包含 Content-Type 请求标头(作为 message/ohttp-req)。


POST https://safebrowsingohttpgateway.googleapis.com/v1/ohttp:handleOhttpEncapsulatedRequest?key=<API key>

注意:根据 RFC 指南,使用 Binary HTTP 协议 RFC 9292 对内部请求进行编码(请参阅 V5 文档,了解如何构建安全浏览请求)。

客户端库

Google Quiche 具备同时支持 OHTTPBHTTP 协议的客户端实现。建议客户端使用这些库。请参阅以下伪代码,了解如何构建 OHTTP 请求以访问 API。

客户端实现示例

客户端从 public key 端点提取 Oblivious HTTP 公钥。随后像这样初始化 quiche OHTTP 密钥配置,并初始化 quiche OHTTP 客户端。


auto ohttp_key_cfgs = quiche::ObliviousHttpKeyConfigs::ParseConcatenatedKeys(std::string public_key); auto key_config = ohttp_key_cfgs->PreferredConfig(); auto public_key = ohttp_key_cfgs->GetPublicKeyForId(key_config.GetKeyId()) auto ohttp_client = quiche::ObliviousHttpClient::Create(public_key, key_config);

在加密之前,客户端将使用二进制 HTTP 编码创建 BHTTP 请求。


quiche::BinaryHttpRequest::ControlData bhttp_ctrl_data{ .method = "POST", .scheme = "https", .authority = "safebrowsing.googleapis.com", .path = "/v5/hashes:search?key=<API key>&hashPrefixes=<HASH prefix 1>&hashPrefixes=<HASH prefix 2>", }; quiche::BinaryHttpRequest bhttp_request(bhttp_ctrl_data);

客户端随后会对在上述步骤中创建的二进制 HTTP 请求进行加密。


auto bhttp_serialized = bhttp_request.Serialize(); auto ohttp_request = ohttp_client.CreateObliviousHttpRequest(*bhttp_serialized); // Client must include this in POST body, and add `Content-Type` header as "message/ohttp-req". auto payload_include_in_post_body = ohttp_request.EncapsulateAndSerialize();

收到来自 Relay 的响应后,客户端会对响应进行解密。响应将包含 Content-Type 响应标头作为 ohttp-res


auto ctx = std::move(ohttp_request).ReleaseContext(); auto ohttp_response = ohttp_client.DecryptObliviousHttpResponse("data included in body of http_response", ctx);

成功解密 OHTTP 响应后,使用二进制 HTTP 解码输出,如下所示。


auto bhttp_response = BinaryHttpResponse::Create(ohttp_response.GetPlaintextData()); if (bhttp_response.status_code() == 200) { auto http_response = bhttp_response.body(); auto response_headers = bhttp_response.GetHeaderFields(); }