Overview

安全浏览 Oblivious HTTP Gateway API

注意:本文档目前仍处于开发阶段。预计在不久的将来会有所改进。

Safe Browsing Oblivious HTTP Gateway API 是一种隐私保护 API,基于名为 Oblivious HTTPRFC 9458 的 IETF RFC 协议而构建。

概览

Safe Browsing Oblivious HTTP Gateway API 是 Google 的一项服务,可让客户端应用根据 Google 不断更新的不安全网络资源列表来检查网址,并实施额外的隐私保护措施。

这是通过名为 Oblivious HTTP(简称 OHTTP)的轻量级协议实现的。安全浏览客户端可以使用这种无状态协议来访问 Google Safe Browsing V5 API,从而在不影响用户保护隐私。

注意:无法通过此服务访问 Google Safe Browsing V4 API

安全浏览 Oblivious HTTP 协议

RFC 协议

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

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

服务的整体架构。
: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 请求,并随后对 OHTTP 响应进行加密,以便在 HTTP 响应中转发回 Relay。客户端必须将 Content-Type 请求标头作为 message/ohttp-req 添加到 HTTP POST 请求中。


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 的响应后,客户端将对响应进行解密。该响应将包含 ohttp-res 形式的 Content-Type 响应标头。


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(); }