들어가며: 왜 프라이버시 보존형 URL 검사가 필요한가?
메신저에서 링크를 클릭하는 순간, 그 링크가 안전한지 서버가 검사하려면 '어떤 링크를 클릭했는지'를 서버가 알게 됩니다. 종단간 암호화(E2EE)로 대화 내용을 보호하더라도, 링크 자체가 서버에 노출되면 프라이버시가 깨질 위험이 있습니다.
Meta(페이스북)의 Messenger는 이 문제를 해결하기 위해 Advanced Browsing Protection (ABP) 기능을 도입했습니다. ABP는 사용자가 클릭한 URL이 악성 사이트인지 검사하면서도, 서버가 그 URL의 내용을 알 수 없도록 설계된 시스템입니다. 이 글에서는 ABP의 핵심 기술들을 하나씩 뜯어보겠습니다.
참고 자료: 이 글은 Meta Engineering 블로그의 How Advanced Browsing Protection Works in Messenger를 기반으로 분석했습니다.

ABP의 기술 스택: PIR, OPRF, TEE, ORAM의 조화
ABP는 여러 암호학적 프리미티브와 인프라 기술을 계층적으로 결합합니다. 각 기술이 어떤 역할을 하는지 살펴보겠습니다.
1. Private Information Retrieval (PIR) — 쿼리의 시작점
PIR은 클라이언트가 서버에 "이 데이터가 DB에 있나요?"라고 물을 때, 서버가 무엇을 물었는지 전혀 알 수 없게 하는 기술입니다. 이상적인 PIR은 서버가 전체 DB를 클라이언트에게 보내주면 해결되지만, ABP의 DB는 수백만 개의 악성 URL을 포함하고 자주 업데이트되므로 현실적이지 않습니다.
2. Oblivious Pseudorandom Function (OPRF) — 정확하지 않은 매칭의 문제
ABP가 처음 고려한 접근법은 OPRF를 사용해 DB를 여러 개의 버킷(bucket)으로 나누는 것이었습니다. 하지만 OPRF는 정확히 일치하는(exact match) 쿼리에 최적화되어 있습니다. URL 검사는 'example.com'이 DB에 있을 때 'example.com/a/b/index.html'도 매칭되어야 하는 프리픽스(prefix) 매칭이 필요합니다.
이를 해결하기 위해 클라이언트가 URL의 모든 경로 프리픽스에 대해 PIR 쿼리를 병렬로 보내는 방법을 생각할 수 있습니다. 하지만 이렇게 하면 서버가 클라이언트의 실제 URL에 대한 정보를 더 많이 알게 됩니다(프리픽스 개수만큼 정보 누출).
3. Ruleset을 이용한 버킷 밸런싱
이 문제를 해결하기 위해 Meta는 Ruleset이라는 전처리 단계를 도입했습니다. 서버는 DB를 주기적으로 분석해, URL을 해싱할 때 도메인만 사용하지 않고 특정 경로 세그먼트를 추가로 포함하도록 하는 규칙을 생성합니다.
# Ruleset 예시 (의사 코드)
ruleset = [
{"hash_prefix": "08bd4dd11758b503", "path_segments": 2},
{"hash_prefix": "fe891588d205cf7f", "path_segments": 1},
{"hash_prefix": "c078e5ff2e262830", "path_segments": 4},
]
def get_bucket_id(url, ruleset):
"""Ruleset을 적용하여 URL의 버킷 ID를 계산"""
current_hash = hash(url.domain)
while True:
rule = find_matching_rule(current_hash, ruleset)
if rule is None:
break
# 규칙에 따라 경로 세그먼트를 추가하여 다시 해싱
current_hash = hash(url.domain + "/" + get_path_segments(url, rule.path_segments))
return current_hash[:2] # 첫 2바이트를 버킷 ID로 사용
이 방식을 통해 서버는 버킷 크기를 균일하게 유지하면서도, 클라이언트가 보내는 버킷 ID가 URL의 전체 경로를 노출하지 않도록 합니다.
4. Confidential Computing (TEE) — 하드웨어 수준의 격리
버킷 ID조차 서버 운영자에게 노출되는 것을 막기 위해, Meta는 AMD SEV-SNP 기반의 신뢰 실행 환경(Confidential Virtual Machine, CVM)을 사용합니다. 클라이언트는 CVM의 증명 보고서(attestation report)를 검증한 후, 안전한 채널을 통해 버킷 ID를 CVM 내부로 전송합니다. CVM 외부의 프로세스는 이 ID를 볼 수 없습니다.
5. Oblivious RAM (ORAM) — 메모리 접근 패턴 은닉
TEE가 메모리를 암호화하더라도, 메모리 접근 패턴 자체는 관찰될 수 있습니다. 예를 들어 서버가 항상 버킷 5번만 읽는다면, 공격자는 어떤 버킷이 조회되었는지 유추할 수 있습니다. 이를 방지하기 위해 ABP는 Path ORAM 알고리즘을 사용해 모든 요청에서 모든 버킷을 읽는 것처럼 보이게 만듭니다.
# ORAM을 사용한 선형 스캔 시뮬레이션 (개념 코드)
class OramBucketAccess:
def __init__(self, num_buckets):
self.num_buckets = num_buckets
self.buckets = [None] * num_buckets
def read_bucket_privately(self, target_index):
"""
모든 버킷을 읽지만 실제로는 target_index만 반환.
메모리 접근 패턴을 숨기기 위해 Path ORAM 사용.
"""
# 실제로는 Path ORAM 트리를 탐색하지만,
# 개념적으로는 아래처럼 모든 버킷을 순회
result = None
for i in range(self.num_buckets):
data = self._access_memory(i)
if i == target_index:
result = data
return result
def _access_memory(self, index):
# 하드웨어 메모리 접근 (실제로는 암호화된 상태)
return self.buckets[index]
6. Oblivious HTTP (OHTTP) — IP 주소 은닉
마지막으로, 클라이언트의 IP 주소 등 식별 정보를 서버로부터 숨기기 위해 ABP는 Oblivious HTTP 프록시를 사용합니다. 클라이언트는 요청을 암호화하여 제3자 프록시에 보내고, 프록시는 식별 정보를 제거한 후 서버로 전달합니다. 서버는 누가 요청했는지 알 수 없습니다.

ABP의 한계와 주의사항
ABP는 매우 정교한 시스템이지만, 몇 가지 중요한 한계가 있습니다.
-
성능 오버헤드: 모든 버킷을 스캔하는 ORAM 접근은 계산 비용이 큽니다. Meta는 DB 크기가 아직 충분히 작아 여러 복사본을 메모리에 올려 병렬 처리를 통해 이 문제를 완화하고 있습니다.
-
완벽한 프라이버시는 아님: OHTTP 프록시는 제3자이므로, 프록시와 서버가 공모하면 프라이버시가 깨질 수 있습니다. 또한 TEE의 증명 체인에 대한 신뢰가 필요합니다.
-
업데이트 지연: Ruleset과 DB 버킷은 주기적으로 업데이트되므로, 새로운 악성 URL이 등록되기까지 시간차가 발생할 수 있습니다.
-
국내 환경 적용 시 고려사항: 한국의 카카오톡, 라인 등도 유사한 기능을 도입할 경우, TEE 인프라 구축 비용과 OHTTP 프록시의 법적/규제 이슈(개인정보보호법, 통신비밀보호법)를 함께 고려해야 합니다. 특히 OHTTP 프록시를 국내에 둘 경우 프록시 운영자의 신뢰성과 데이터 보호 조치가 중요합니다.

결론: 프라이버시와 보안의 절충, 그리고 미래
Meta의 ABP는 암호학과 시스템 보안의 최신 기술을 종합하여, 사용자 프라이버시를 침해하지 않으면서 악성 링크를 탐지하는 실용적인 솔루션을 제시합니다. PIR, OPRF, TEE, ORAM, OHTTP 각각의 기술은 완벽하지 않지만, 이들을 결합함으로써 단일 기술의 한계를 극복하고 있습니다.
다음 단계 학습 방향
- PIR/OPRF 심화: Cryptographic Private Information Retrieval (Wikipedia)를 읽어보세요.
- TEE 실습: AWS Nitro Enclaves나 AMD SEV-SNP를 활용한 간단한 애플리케이션을 직접 배포해보세요.
- ORAM 구현: Meta가 공개한 Path ORAM 라이브러리를 살펴보세요.
함께 보면 좋은 글