Overview

세이프 브라우징 무시 HTTP Gateway API

참고: 이 문서는 현재 개발 중입니다. 조만간 개선될 예정입니다.

Safe Browsing Oblivious HTTP Gateway API는 Oblivious HTTP, RFC 9458이라는 IETF RFC 프로토콜 위에 구축된 개인 정보 보호 API입니다.

개요

Safe Browsing Oblivious HTTP Gateway API는 추가 개인정보 보호 기능을 사용하여 클라이언트 애플리케이션이 지속적으로 업데이트되는 Google의 안전하지 않은 웹 리소스 목록과 비교하여 URL을 확인할 수 있도록 하는 Google 서비스입니다.

이 기능은 Oblivious HTTP 또는 줄여서 OHTTP라고 하는 경량 프로토콜을 통해 작동합니다. 이는 세이프 브라우징 클라이언트가 Google Safe Browsing V5 API에 액세스하기 위해 사용하는 스테이트리스(Stateless) 프로토콜로, 사용자의 개인 정보를 보호하면서도 강력한 보호 기능을 얻고 노출 범위를 확대하는 데 사용됩니다.

참고: 이 서비스를 통해 Google 세이프 브라우징 V4 API에 액세스할 수 없습니다.

세이프 브라우징 무시 HTTP 프로토콜

RFC 프로토콜

Oblivious HTTP는 RFC 9458에 정의된 경량 프로토콜로, 클라이언트에서 대상 서버로 HTTP 메시지를 암호화하여 전송하는 데 사용됩니다. 신뢰할 수 있는 릴레이 서비스를 사용하여 대상 서버에서 클라이언트 식별을 위한 IP 주소, 연결 정보 등의 메타데이터 사용을 완화하는 방식으로 일반 HTTP/S 프로토콜에 기반한 개인 정보 보호 및 보안을 제공합니다. 이 프로토콜은 RFC 9292에 정의된 바이너리 HTTP를 사용하여 HTTP 요청/응답을 인코딩/디코딩합니다.

개략적으로 Relay는 클라이언트와 게이트웨이 리소스 사이에 위치하며 IP 주소와 같은 개인 정보 보호에 민감한 속성을 비롯하여 모든 클라이언트 식별자를 삭제하여 게이트웨이 서비스로 수신되는 HTTP 요청을 효과적으로 익명화합니다. OHTTP의 추가적인 이점은 모든 요청이 엔드 투 엔드 암호화된다는 것입니다. 즉, 클라이언트의 세이프 브라우징 쿼리 (예: URL 표현식의 잘린 해시)가 Relay에 표시되지 않습니다. Chrome에서 구현한 예는 블로그 게시물을 참고하세요.

서비스의 전체 아키텍처입니다.
그림: OHTTP 흐름

클라이언트는 어떤 Relay 제공업체 (예: 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의 안내에 따라 바이너리 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에서 응답을 수신하면 클라이언트는 응답을 복호화합니다. 응답에는 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(); }