독일 출구(exit)로 프록시를 설정하고 수집 작업을 실행했는데, 대상 사이트가 계속 엉뚱한 지역용 콘텐츠를 제공한다. 출구 주소는 독일로 확인되고 요청도 성공하는데, 결과는 마치 서버가 실제로 위치한 곳에서 온 것처럼 보인다. 흔한 원인은 연결은 프록시를 통과했지만 이름 해석(name resolution)은 그렇지 않았다는 것이다.
이것이 바로 DNS 누출이다. 프라이버시 맥락에서는 리졸버가 사용자가 방문하는 도메인을 볼 수 있다는 문제로 설명되며 이는 실제 문제이지만, 데이터 수집에서는 더 직접적인 결과가 있다. DNS 해석은 종종 지리적 인식을 갖고 있어서, 로컬에서 해석하면서 원격으로 나가면 불일치가 발생하고, 이는 잘못된 지역 엔드포인트를 반환하여 지오 타겟팅된 데이터셋을 조용히 오염시킬 수 있다.
실제로 무엇이 누출되며 왜 중요한가
호스트네임에 대한 요청은 두 단계를 거친다. 이름을 주소로 변환하는 것과 그 주소로 연결하는 것이다. 프록시는 두 번째 단계를 가로챈다. 첫 번째 단계까지 가로채는지는 전적으로 클라이언트에 달려 있다.
클라이언트가 로컬에서 해석하면, DNS 쿼리는 사용자 자신의 네트워크에서 자신의 리졸버로 나가며, 이는 도메인을 ISP나 리졸버 운영자에게 노출시키고, 사용자의 위치에 맞춰 계산된 답을 반환한다.
바로 이 마지막 부분이 수집 작업을 망가뜨린다. 대형 사이트들은 CDN과 지오 인식 DNS 뒤에 있어서 쿼리가 어디서 왔는지에 따라 다른 주소를 반환하므로, 로컬에서 해석된 조회는 프록시 출구 근처가 아니라 사용자 서버 근처의 엣지 노드를 가리킬 수 있다. 그런 다음 독일 주소를 통해 그 노드에 연결하게 되고, 이 불일치는 잘못된 지역의 콘텐츠, 실행마다 일관되지 않은 결과, 또는 대상 사이트에 이상하게 보이는 신호를 만들어낼 수 있다. 작업이 도시 단위 타겟팅에 의존한다면, 지리적 위치가 잘못된 것으로 보일 때 이 실패 유형을 가장 먼저 배제해봐야 하며, 다른 흔한 원인인 지오로케이션 데이터베이스 차이도 함께 확인해야 한다.
HTTP 프록시는 거의 누출되지 않지만 SOCKS5는 자주 누출된다
이 동작은 프로토콜별로 나뉘며, 이것이 문제의 핵심이다.
HTTP 프록시의 경우, HTTPS 요청은 CONNECT를 사용하고 호스트네임을 프록시에 전달하여 프록시가 원격으로 해석한다. 일반 HTTP도 마찬가지로 호스트네임을 포함한 절대 URL을 전송한다. 두 경우 모두 프록시가 설계상 조회를 수행하므로, 일반적인 HTTP 프록시 사용은 DNS를 누출하지 않는다.
SOCKS5의 경우, 프로토콜이 두 가지 방식을 모두 지원한다. 호스트네임을 받아 원격으로 해석할 수도 있고, 클라이언트가 직접 해석한 주소를 받을 수도 있다. 어느 쪽이 일어날지는 클라이언트의 결정이며, 많은 클라이언트가 기본값으로 로컬 해석을 선택한다. 이것이 바로 동일한 게이트웨이가 한 설정에서는 누출되고 다른 설정에서는 누출되지 않는 이유이며, 이는 거의 항상 프록시 쪽 문제가 아니라 클라이언트 쪽 설정 문제다.
대부분의 도구에서 이 차이는 문자 하나로 결정된다. socks5://는 로컬에서 해석한다는 의미이고, socks5h://는 호스트네임을 프록시에 넘긴다는 의미다. h 하나가 전체 해결책이다.
클라이언트별 수정 방법
curl. socks5 대신 socks5h 스킴을 사용하거나, --proxy에 해당 스킴을 지정한다:
# 누출됨: 로컬에서 해석
curl -x socks5://customer-USERNAME-country-de:PASSWORD@p.shifter.io:443 https://example.com
# 올바름: 프록시에서 호스트네임 해석
curl -x socks5h://customer-USERNAME-country-de:PASSWORD@p.shifter.io:443 https://example.com
Python. requests와 PySocks에서는 스킴이 동일한 의미를 가지며, 두 표기가 모두 작동하기 때문에 실수하기 쉽다:
import requests
user = "customer-USERNAME-country-de"
PROXY = f"socks5h://{user}:PASSWORD@p.shifter.io:443" # h에 주목
r = requests.get("https://example.com",
proxies={"http": PROXY, "https": PROXY}, timeout=20)
가장 간단한 방법은 동일한 게이트웨이에 대해 HTTP 스킴을 사용하는 것이다. 이는 기본적으로 원격 해석을 하므로 이 문제 자체를 피할 수 있다. 이것이 Python으로 레지덴셜 프록시 사용하기에서 다루는 설정이다.
Node. socks 에이전트는 대체로 원격 해석을 위한 플래그를 제공한다. 라이브러리와 버전에 따라 기본값이 다르므로, 가정하지 말고 활성화 여부를 확인하라.
헤드리스 브라우저. 여기서 누출이 가장 발생하기 쉬운데, 브라우저는 프록시 설정과 무관하게 자체 리졸버 동작을 갖고 있기 때문이다. Chromium은 자체 스택을 통해 해석하므로 SOCKS 사용 시 DNS 경로를 명시적으로 제한해야 한다. Firefox에는 SOCKS 호스트네임을 원격으로 해석할지 제어하는 설정이 있는데, 기본적으로 항상 켜져 있는 것은 아니다. 브라우저를 직접 제어한다면 가정하지 말고 검증하라. 아래의 브라우저 특유의 누출 경로도 참고하라.
실제로 누출되는지 테스트하기
설정만으로 판단하지 말라. 세 가지 수준의 확인 방법이 있다.
가장 빠른 방법은 대상 사이트가 보는 것과 사용자가 기대하는 것을 비교하는 것이다. 요청자의 주소를 보고하는 엔드포인트를 조회하여 출구 지역과 일치하는지 확인한 다음, 지오 민감 페이지를 조회하여 콘텐츠가 일치하는지 확인한다. 주소는 맞는데 콘텐츠가 틀리다면 DNS가 유력한 용의자다.
더 직접적인 방법은 프록시를 통한 요청이 실행되는 동안 아웃바운드 DNS 트래픽을 관찰하는 것이다. 요청을 보내는 머신에서 포트 53으로 대상 호스트네임에 대한 쿼리가 나가는 것이 보인다면, 해석이 로컬에서 일어나고 있는 것이다:
# 터미널 1
sudo tcpdump -n -i any port 53
# 터미널 2
curl -x socks5h://customer-USERNAME-country-de:PASSWORD@p.shifter.io:443 https://example.com
캡처에서 대상 호스트네임에 대한 쿼리가 나타나면, 클라이언트가 로컬에서 해석한 것이며 누출을 발견한 것이다.
셋째, 브라우저 자동화의 경우 공개 DNS 누출 테스트 페이지 중 하나를 사용하라. 이는 사용자를 대신해 응답한 리졸버를 보고하며, 이들이 사용자 출구의 지역에 있는지 아니면 사용자 자신의 지역에 있는지 확인할 수 있다.
알아둘 만한 다른 누출 경로
DNS가 가장 흔한 경우지만, 완전성을 위해 같은 종류의 실패를 일으키는 세 가지를 더 짚어본다.
IPv6. 프록시가 IPv4만 처리하는데 시스템이 듀얼 스택 대상에 대해 IPv6을 선호한다면, 그 트래픽은 프록시를 완전히 우회할 수 있다. 수집 작업을 하는 머신에서 IPv6을 비활성화하거나 클라이언트에서 IPv4를 강제하면 혼란스러운 결과의 전체 범주를 제거할 수 있다.
WebRTC. 실제 브라우저에서 WebRTC는 프록시 설정을 무시하는 별도의 메커니즘을 통해 로컬 및 공개 주소를 노출할 수 있다. 신원과 관련해 민감한 작업을 위해 브라우저를 조작하고 있다면 이를 비활성화하라.
시스템 및 라이브러리 기본값. 일부 런타임은 DNS 결과를 적극적으로 캐싱하거나 프록시 설정과 무관하게 OS 리졸버를 참조하며, hosts 파일 항목이나 로컬 캐싱 리졸버는 사용자가 설정한 모든 것을 무시할 수 있다.
작업에서 신원 일관성이 중요하다면, DNS는 여러 계층 중 하나일 뿐이며, 함께 일치해야 하는 더 넓은 신호 집합은 TLS 및 HTTP/2 핑거프린팅과 차단 회피하기에서 다룬다.
빠른 체크리스트
SOCKS5가 특별히 필요한 경우가 아니라면 게이트웨이에 대해 HTTP 스킴을 우선 사용하라. 이는 기본적으로 원격 해석을 하기 때문이다. SOCKS5를 사용한다면 모든 곳에서 socks5h를 사용하고, 코드베이스에서 순수한 socks5://를 grep으로 찾아 놓친 것이 없는지 확인하라. 설정을 신뢰하지 말고 포트 53에서의 패킷 캡처로 검증하라. 프록시 경로가 IPv4 전용이라면 IPv4를 강제하라. 자동화된 브라우저에서는 WebRTC를 비활성화하라. 그런 다음 지오 민감 요청을 다시 실행하여 콘텐츠가 사용자 자신의 지역이 아니라 출구 지역과 일치하는지 확인하라.
결론
DNS 누출이란 트래픽은 프록시를 통과했지만 이름 해석은 그렇지 않았다는 것을 의미하며, 이는 사용자가 조회하는 도메인을 자신의 리졸버에 노출시키고, 수집 작업에서 더 중요하게는 사용자의 출구가 아니라 사용자의 위치에서 도메인을 해석하게 만든다. HTTP 프록시는 기본적으로 원격 해석을 하므로 거의 누출되지 않으며, SOCKS5는 클라이언트가 로컬에서 해석할 때마다 누출된다. 이것이 socks5h 대 socks5가 가장 흔한 해결책인 이유다. 헤드리스 브라우저는 프록시 설정과 무관하게 독립적으로 해석하기 때문에 명시적인 주의가 필요하다. 설정을 신뢰하는 대신 패킷 캡처로 테스트하고, IPv6과 WebRTC도 함께 배제하라. 지오 타겟팅된 작업이 잘못된 지역의 콘텐츠를 반환하는데 출구 주소는 올바르게 보인다면, 이것이 가장 먼저 확인해야 할 사항이다.
게이트웨이는 동일한 엔드포인트에서 HTTP(S)와 SOCKS5를 모두 지원하므로, 테스트를 위해 스킴을 전환하는 데 비용이 들지 않는다. 게이트웨이 및 인증을 참고하라. 제품은 국가 및 도시 타겟팅과 GB당 요금제를 지원하는 레지덴셜 프록시이다.