Overview

セーフ ブラウジング: Oblivious HTTP Gateway API

注: このドキュメントは現在開発中です。今後の改善を予定しています。

Safe Browsing Oblivious HTTP Gateway API は、Oblivious HTTPRFC 9458 という名前の IETF RFC プロトコル上に構築されたプライバシー保護 API です。

概要

Safe Browsing Oblivious HTTP Gateway API は、Google が常時更新している安全でないウェブリソースのリストに照らし合わせて、クライアント アプリケーションが URL をチェックできるようにする Google サービスで、プライバシー保護が追加されています。

これは、Oblivious HTTP(略して OHTTP)と呼ばれる軽量のプロトコルで実現します。このステートレス プロトコルは、セーフ ブラウジング クライアントが Google セーフ ブラウジング V5 API にアクセスするために使用できるもので、ユーザーのプライバシーを損なうことなく堅牢な保護とカバレッジの拡大を実現します。

注: このサービスから Google セーフ ブラウジング V4 API にアクセスすることはできません。

セーフ ブラウジング 自明の HTTP プロトコル

RFC プロトコル

Oblivious HTTP は、RFC 9458 で定義されている軽量のプロトコルで、クライアントからターゲット サーバーへの HTTP メッセージの暗号化と送信に使用されます。これは、クライアント識別のためにターゲット サーバーが IP アドレスや接続情報などのメタデータを使用するのを緩和する方法で信頼できるリレーサービスを使用し、プレーン HTTP/S プロトコルに加えてプライバシーとセキュリティを提供します。このプロトコルは、RFC 9292 で定義されているバイナリ HTTP を使用して、HTTP リクエスト/レスポンスのエンコード/デコードを行います。

大まかに言うと、Relay は、クライアント トラフィックをプロキシするクライアント リソースと Gateway リソースの間に位置し、IP アドレスなどのプライバシーに配慮する属性を含むすべてのクライアント識別子を削除し、Gateway サービスへの HTTP 受信リクエストを効果的に匿名化します。OHTTP のもう一つのメリットは、すべてのリクエストがエンドツーエンドで暗号化されることです。つまり、クライアントのセーフ ブラウジング クエリ(URL 式の切り捨てられたハッシュ)が 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 キーの設定方法をご覧ください。

プライバシーに関する推奨事項のセクションで説明したように、鍵の整合性の目標を達成するために、クライアント ベンダーは、このエンドポイントから鍵を取得してクライアント アプリケーションに配布するために、一元化された鍵配布インフラストラクチャを設定することが推奨されます。

鍵管理ガイダンスに従って、鍵はサーバー上で定期的にローテーションされます。クライアントは鍵を更新する(つまり、復号の失敗を避けるために、鍵のローカルコピーをときどきフェッチして更新する必要があります)。

クライアントは公開鍵を 1 日 1 回更新(フェッチして更新)する必要があります。一元的な分散メカニズムが使用されている場合、このメカニズムによって、キーを 1 日に 1 回フェッチして配布する必要があります。

OHTTP カプセル化リクエスト

このエンドポイントは、リクエストを復号することで、POST リクエストの HTTP 本文に含まれる OHTTP リクエストを処理し、その後、HTTP レスポンスの OHTTP レスポンスを暗号化して Relay に戻します。クライアントは、HTTP POST リクエストに message/ohttp-req として Content-Type リクエスト ヘッダーを含める必要があります。


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

注: RFC のガイダンスに沿って、バイナリ HTTP プロトコル(RFC 9292)を使用して内部リクエストをエンコードします(セーフ ブラウジング リクエストの作成方法については、V5 のドキュメントをご覧ください)。

クライアント ライブラリ

Google Quiche には、OHTTPBHTTP プロトコルの両方のクライアント側の実装があります。クライアントは、これらのライブラリを使用することをおすすめします。API にアクセスするために OHTTP リクエストを構築する方法については、以下の疑似コードを参照してください。

クライアント側の実装例

クライアントは、公開鍵エンドポイントから 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(); }