payroll.internal.acme-corp.example를 조회하면, 예전엔 루트 서버가 그 전체를 봤다. .example TLD 운영자도 봤다. 둘 다 그게 필요 없었는데도. 그 거래에서 루트 서버의 유일한 일은 “나는 .example을 운영하지 않으니 이 네임서버에 물어보라”고 말하는 것이다. example이라는 단어만 알아도 그 답을 줄 수 있었다. 그런데 약 30년 동안 리졸버는 내려가는 길에 만나는 모든 서버에 전체 이름을 넘겼다.
고전적 DNS 해석에서 늘 거슬렸던 게 그 부분이다. 안전하지 않다는 게 아니라 — 그건 DNSSEC이 다룬다 — 쓸데없이 수다스럽다는 것. 위임 체인을 걷는 재귀 리졸버는 전체 질의 이름을 루트에 보내고, 또 전체 이름을 TLD에 보내고, 또 각 권한 서버에 보냈다. 마지막 서버를 뺀 모든 서버는 다음 홉을 가리키는 데 필요한 만큼의 이름만 있으면 됐는데도. 당신은 아무런 운영상 이유 없이, 루트 운영자와 TLD 운영자에게 무엇을 조회하는지 전부, 낱낱이 말하고 있었다.
QNAME 최소화가 그 해결책이고, 흥미로운 건 메커니즘이 아니다. 그 메커니즘이 실제로 배포됐다는 점이다.
리졸버가 말하기를 멈춘 것
발상은 민망할 만큼 단순하다. 리졸버가 체인을 걸을 때, 각 서버에는 그 서버가 위임을 주는 데 실제로 필요한 레이블만 보내야 한다 — 전체 이름이 아니라.
그래서 루트에 payroll.internal.acme-corp.example를 묻는 대신, 리졸버는 루트에 example만 묻는다: 그것의 네임서버를 달라. 루트는 .example 서버를 가리킨다. 이제 그 서버들에 acme-corp.example를 묻는다 — 레이블 하나 더 길지만 여전히 전체 이름은 아니다. 그들이 Acme의 네임서버를 가리킨다. 그제서야, 실제로 그 존을 호스팅하며 어차피 답할 권한 서버에 이르러서, 전체 payroll.internal.acme-corp.example가 전달된다. 봐야만 하는 서버가 본다. 상류의 모두는 자기 일에 필요한 만큼만 본다.
와이어 포맷은 전혀 바뀌지 않는다. 권한 서버는 아무것도 할 필요가 없다. 클라이언트도 아무것도 할 필요가 없다. 이건 소프트웨어 한 조각 — 가운데의 재귀 리졸버 — 이 질문을 어떻게 표현할지 정하는 방식의 변화다. 바로 그 속성이 이야기의 전부이고, 뒤에서 다시 다룬다.
이건 2016년 3월 RFC 7816으로, Stéphane Bortzmeyer가 실험(Experimental) 프로토콜로 정리했다. “실험”은 “우리는 이게 맞다고 보지만 실제 인터넷과 부딪혀 살아남는 걸 먼저 보고 싶다”는 IETF식 표현이다. 첫 시도에 온전히 살아남지는 못했다.
깨진 부분, 그리고 그걸 고친 방법
원래 명세의 엄격한 해석은 이랬다: 다음 위임을 찾으려면 각 중간 이름에서 NS 레코드를 질의하라. 이론상 깔끔하다. 실제로는, 우울할 만큼 많은 권한 서버가 중간 레이블로만 존재하는 이름 — 자기 레코드가 없는 internal.acme-corp.example — 에 대한 질의에 위임 대신 NXDOMAIN(“그런 이름 없음”)으로 답한다. 그 답은 틀렸다. 하지만 DNS엔 자신 있게 틀린 서버가 가득하고, NXDOMAIN을 액면 그대로 받아들인 리졸버는 조회 전체가 실패했다고 판단해, 멀쩡히 해석되는 이름에 대해 사용자에게 오류를 던진다.
그래서 초기 엄격 모드엔 실비용이 있었다: 켜면, 규정을 안 지키는 서버를 상대로 정당한 조회의 일부가 깨졌다. 그건 채택을 죽이는 종류의 세금이다. 리졸버 운영자는 지원 티켓을 뒤집어쓰는데 프라이버시 이득은 눈에 안 보이니까.
2021년 11월 나온 RFC 9156이 어른 버전이다. 7816을 폐기하고, 이 기법을 표준화 트랙으로 올리고 — 결정적으로 — 완화(relaxed) 모드를 합리적 기본값으로 접어 넣었다. 완화 최소화도 이름을 여전히 다듬지만, 깨진 서버를 견디는 질의 타입과 폴백 동작을 쓴다: 최소화된 질의가 말이 되는 답을 못 받으면, 리졸버는 사용자를 실패시키는 대신 조용히 이름을 더 많이 보내는 쪽으로 물러난다. 프라이버시 대부분을 지키면서 장애 생성을 멈춘다. 그 맞바꿈 — 순수성을 조금 덜고 장애를 크게 던 것 — 이 이걸 배포 가능하게 만들었다.
아무도 동의할 필요가 없어서 배포됐다
여기 중요한 숫자가 있다. SIDN Labs의 한 측정 연구가 실제 DNS 트래픽 중 얼마가 최소화됐는지 추적했다: 2018년 질의의 약 1.6%, 2022년엔 약 16%까지. 같은 기간 최소화를 수행하는 오픈 리졸버 수는 약 18,000에서 약 80,000으로 늘었다. 보편적이진 않다. 하지만 4년 만에 열 배 도약을, 조용히, 마케팅 캠페인도 규정 강제도 없이.
DNS 보안 기능들의 무덤과 비교해 보라. DNSSEC은 20년이 넘었는데도 세계 조회의 소수만 검증한다. DANE, MTA-STS, CAA — 각각 진짜 문제를 풀고, 각각 수년간 나와 있었고, 각각 반올림 오차 수준의 도메인에만 배포돼 있다. 왜인지는 전에 쓴 적 있다: 그 프로토콜들은 협조를 요구한다. DNSSEC은 존 운영자와 등록기관과 리졸버가 함께 움직여야 하고, 단 하나의 실수를 죽은 도메인으로 처벌한다. MTA-STS는 양쪽 메일 제공자가 다 신경 써야 한다. 보안 개선이 둘 이상의 독립 당사자가 발맞춰 행동하기를 요구하면, 인터넷의 답은 대개 “싫어”다.
QNAME 최소화가 요구한 건 정확히 한 당사자의 행동이었다: 리졸버 운영자. 조회 대상 도메인도, 등록기관도, 권한 서버도, 최종 사용자도, 브라우저 벤더도 아니다. 운영자 한 명이 설정 하나를 켜면 — 더 낫게는, 기본값으로 켜진 버전을 배포하면 — 그 장비를 지나는 모든 질의가, 뒤에 있는 모든 사용자를 위해, 눈에 안 보이게 더 사적으로 바뀐다. 협상이 없다. 상대방이 없으니까. 인터넷의 권한 서버 쪽은 이게 일어나고 있다는 것조차 모른다.
그래서 리졸버 소프트웨어는 그냥… 했다. Unbound, BIND, Knot Resolver, PowerDNS 모두 최소화를 구현하고 기본값으로 켰다. Cloudflare의 1.1.1.1은 완전한 최소화를 한다. Google Public DNS는 부분 버전을 한다 — 끝까지가 아니라 TLD 레벨까지 최소화한다 — 이건 그 자체로 작은 교훈이다: “성공”조차 고르지 않고, 특정 리졸버가 RFC가 허용하는 만큼 당신을 보호한다고 가정할 수 없다. 하지만 궤적은 DNSSEC이 끝내 못 가진 그것이다: 기본값 켜짐, 이미 모두가 돌리는 소프트웨어 안에서, 정상회담 없이.
하지 못하는 것
최소화는 암호화가 아니다. 둘이 자주 뒤섞이니 정확히 짚어둘 가치가 있다. DNS-over-HTTPS나 DNS-over-TLS를 쓰지 않는 한 당신의 질의는 여전히 평문으로 오간다. 당신 리졸버와 서버들 사이 경로에 있는 누구든 — 그 사이의 네트워크, 호기심 많은 미들박스 — 여전히 무엇을 묻는지 읽을 수 있다. QNAME 최소화는 전송 계층을 건드리지 않는다. 그건 와이어가 읽히는지가 아니라, 어쩔 수 없이 대화해야 하는 권한 서버들 사이에서 누가 무엇을 알게 되는지를 줄인다.
또한 실제로 답하는 서버로부터 전체 이름을 숨기지도 않는다. 존의 권한 네임서버는 여전히 전체 질의를 본다. 봐야 하니까 — 그게 이름을 해석하는 서버다. 당신이 방문하는 존의 운영자가 당신의 방문을 아는 걸 신뢰하지 못한다면, 최소화는 애초에 도움이 될 수 없었다. 그건 다른 도구를 쓰는 다른 문제다. 최소화가 없애는 건 그 밖의 모두에게 가던 불필요한 유출이다: 루트, TLD, 당신의 전체 목적지를 알 이유가 없었는데도 통보받던 모든 중간 위임.
그러니 부분적 해결이다. 이건 DoH/DoT를 대체하는 게 아니라 짝을 이룬다 — 암호화는 질의를 경로로부터 숨기고, 최소화는 방관자 서버들로부터 숨긴다. 둘 다 원해야 한다. 하지만 “실제로 배포된 부분적 해결”이 “20년간 선반에 놓인 완전한 해결”을 이긴다. 그리고 DNS엔 후자가 놓인 긴 선반이 있다.
내가 여기서 거듭 얻는 교훈: 인터넷은 가장 우아한 보안 설계를 보상하지 않는다. 서명이 가장 적게 필요한 것을 보상한다. QNAME 최소화는 누구에게도 허락을 구하지 않았고, 바로 그래서 세계 DNS 질의의 6분의 1 앞에 서 있다 — 더 좋은 평판을 가진 프로토콜들이 아직 다른 모두가 먼저 움직이길 기다리는 동안.