TLS 설정을 DNS로 옮겼다. 그런데 DNS는 서명이 안 돼 있다

HTTPS DNS 레코드는 핸드셰이크가 시작되기도 전에 브라우저에 연결 파라미터를 넘긴다 — 어떤 프로토콜, 어떤 IP, 심지어 ClientHello를 암호화할 키까지. 대부분의 도메인에서, 그건 아무도 서명하지 않은 계층으로 도착한다.

약 30년 동안 규칙은 단순했다: 연결에 대해서는 TLS가 믿어도 된다고 말하기 전까지 아무것도 믿지 않는다. 어떤 암호, 어떤 프로토콜 버전, 상대가 주장하는 그 사람이 맞는지 — 그 전부가 핸드셰이크 안에서, 암호학적 엄호 아래 정해지거나, 아니면 무효였다. DNS는 주소만 건네고 비켜섰다. 믿지 않는 전화번호부였다: 건물을 찾기에는 충분하지만, 절대 열쇠를 맡길 상대는 아닌.

그런데 우리는 그 열쇠를 넘기기 시작했다.

연결을 앞당겨 싣는 레코드

2023년 말 IETF는 RFC 9460을 발행했다. 두 개의 새 DNS 레코드 타입을 정의한다: SVCB(타입 64)와 그 HTTP 전용 형제인 HTTPS(타입 65). 명분은 성능과 프라이버시이고, 두 가지 다 실제로 해낸다. 예전의 춤 — 이름을 A 레코드로 해석하고, 연결을 열고, 전부 인-밴드로 협상하고, 어쩌면 Alt-Svc 헤더로 HTTP/3를 썼어야 했으니 재접속하라는 말을 듣는 — 대신, HTTPS 레코드는 브라우저가 소켓을 하나도 열기 전에 그 전부를 알게 해준다.

이제 하나의 DNS 응답이 브라우저에 이렇게 말할 수 있다: 이 오리진은 HTTP/3를 한다, 두 번째 조회를 건너뛰게 IP 힌트가 여기 있다, 이 포트로 연결하라, 그리고 — 흥미로운 것 — ClientHello를 암호화하는 데 써야 할 공개키가 여기 있다. 그 마지막 필드인 ech 파라미터는 Encrypted Client Hello의 설정을 싣는다. TLS가 늘 흘렸던 단 하나 — 당신이 요청하는 서버 이름 — 을 마침내 숨기는 기능이다.

브라우저는 이미 이걸 한다. Chrome은 2022년부터 프로토콜 발견과 HTTP→HTTPS 업그레이드를 위해 HTTPS 레코드를 기본값으로 존중해 왔고, Firefox와 Safari도 조회한다. 이건 제안이 아니다. 당신의 연결 중 크고 점점 커지는 몫이 이미 이렇게 시작한다.

그리고 이건 진짜로 훌륭한 엔지니어링이다. 연결하기 전에 HTTP/3를 써야 함을 아는 것은 낭비되는 TCP 연결 하나와 Alt-Svc 왕복 하나를 아낀다. ECH 키를 DNS에서 얻는 것은 ECH를 부트스트랩하는 유일하게 실용적인 방법이다 — 요점 자체가 ClientHello를 암호화하는 것이니, 그걸 할 키가 ClientHello 안에서 올 수는 없다. 밖에서 도착해야 한다. DNS는 모든 클라이언트가 이미 조회하는 그 밖의 채널이다. 당연히 키는 거기로 간다.

여기서 멈칫하게 만드는 부분이 있다. 압도적 다수의 도메인에서, 그 DNS 응답은 인증되지 않았다.

우리가 자꾸 서명하지 않기로 하는 계층

DNSSEC — 리졸버가 응답이 변조되지 않았음을 증명할 수 있도록 DNS 레코드를 암호학적으로 서명하는 확장 — 은 1990년대부터 있었고, 그 이후로 줄곧 소수 도메인에만 배포됐다. 채택률 추정치는 존의 4분의 1에 한참 못 미치는 수준을 맴돌고, 그 숫자는 10년째 거의 움직이지 않았다. 인터넷의 대부분 이름은 평범한, 서명되지 않은 DNS로 해석된다: 리졸버가 돌려주는 게 뭐든 클라이언트는 믿는다. 대조할 서명이 없으니까, 서명 자체가 없으니까.

DNS가 어디로 연결할지만 알려줄 때는 그게 견딜 만한 상태였다. 당신의 DNS 경로를 통제하는 공격자가 A 레코드를 위조하면, 당신을 자기가 소유한 기계로 가리킨다 — 하지만 그 기계는 여전히 당신이 요청한 이름에 대해 브라우저가 신뢰하는 인증서를 제시해야 하고, TLS가 거짓말을 잡아낸다. 믿지 않는 전화번호부는 믿는 핸드셰이크가 뒤를 받쳐줬다.

HTTPS 레코드는 그 전화번호부에 실린 것을 조용히 바꾼다. 이제 같은 서명되지 않은 응답이 연결 자체의 파라미터를 실을 수 있다. 그리고 가장 중요한 실패는 극적인 위조가 아니다 — 조용한 뺄셈이다.

ECH를 보자. 그것의 유일한 임무는 서버 이름을 핸드셰이크 안에 암호화해, 경로상의 관찰자가 당신이 어느 사이트를 방문하는지 못 보게 막는 것이다. 그걸 가능케 하는 공개키는 HTTPS 레코드의 ech 필드에 산다. 이제 DNS 경로에 공격자를 놓아보자 — 악의적 리졸버, 뚫린 네트워크, 응답을 재작성하는 미들박스. 그들은 아무것도 위조할 필요가 없다. 그냥 응답이 당신에게 닿기 전에 ech 필드를 지운다.

브라우저는 HTTPS 레코드를 요청하고, ECH 키가 없는 응답을 받고, 유일하게 합리적인 일을 한다: ECH 없이 연결한다. 그럴 수밖에 없다. 대다수 사이트는 ECH 키를 아예 게시하지 않으니, “키 없음”은 경보가 아니라 정상이다. 클라이언트가 실패로 닫을 대상이 없다. 당신을 보호하고 있다고 생각한 프라이버시 기능이, 서명되지 않은 메시지에서 필드 하나를 제거한 것 말고는 아무것도 하지 않은 상대에 의해 꺼진다 — 그리고 당신의 ClientHello는 마치 ECH가 애초에 없었던 것처럼, 서버 이름을 평문으로 내보낸다.

그게 문제의 모양이다. “공격자가 당신의 연결을 위조한다”가 아니다 — 그건 TLS가 여전히 대체로 막는다. “공격자가, 아무도 서명하지 않은 메시지를 편집해서, 당신의 연결을 더 사적으로 만들어야 했던 부분을 다운그레이드한다”이다.

명세는 이걸 내다봤다 — 그게 핵심이다

RFC 9460을 쓴 사람들에게 공정하게 말하면, 이 중 어느 것도 그들에게 놀랄 일이 아니다. 보안 고려사항은 명시적이다: DNS 응답이 암호학적으로 보호되고 SVCB 해석이 변조 냄새가 나는 방식으로 실패하면, 클라이언트는 폴백하지 말고 연결을 포기해야 한다 — 바로 공격자가 파라미터 접근을 막아 다운그레이드를 강제하지 못하게. 내가 방금 묘사한 모든 것에 대한 방어가 적혀 있다. 그 이름은 DNSSEC이다.

바로 그게 이걸 쓸 만한 이야기로 만든다. 완화책은 존재하고, 명세화됐고, 인터넷 대부분에서 켜져 있지 않다. 우리는 프라이버시 보장이 한 계층의 인증에 달린 시스템을 설계하고는, 25년째 집단적으로 인증하지 않기로 해온 계층 위에 그걸 배포했다. 레코드는 존이 서명됐다면 안전하다. 대부분의 존은 서명되지 않았다. 그래서 실제로는 보장이 “암호학적으로 보호됨”에서 “경로상 누군가가 굳이 응답을 편집하지 않는 한 보호됨”으로 열화한다.

이건 일회성이 아니라 패턴이다. 이메일에서 이걸 한 판 했다: SPF, DKIM, DMARC 모두 보안에 결정적인 정책을 DNS TXT 레코드에 게시하고, 그 무결성은 같은 서명되지 않은 조회에 기댄다. 인증서 발급과 CAA 레코드에서도 했다. 몇 년마다 우리는 DNS가 보안을 떠받친다는 새로운 이유를 발견하고, 무게를 실을 레코드 타입을 추가하고, 그 밑의 길이 애초에 포장된 적 없다는 사실을 우회한다.

HTTPS 레코드는 좋은 아이디어다. 연결 설정을 앞당기는 건 옳은 수이고, ECH는 진작 나왔어야 했고, 낭비되는 왕복을 건너뛰는 건 실질적인 사용자 이득이다. 하지만 그것은 우리가 계속 걸고 계속 정산하지 않는 내기의 판돈을 올린다. DNS로 옮기는 모든 능력은 존이 서명된 만큼만 믿을 수 있는 또 하나가 된다 — 그리고 존은, 웹 대부분에서, 여전히 서명되지 않았다. 우리는 왕복 하나를 최적화해 없앤 게 아니다. DNS를 조용히 신뢰 컴퓨팅 기반의 일부로 만들어 놓고, 그것이 주소만 알던 시절과 정확히 똑같이 인증되지 않은 채로 남겨뒀다.

존에 서명하라. 이 모든 기능의 화려하지 않은 나머지 절반이고, 거의 아무도 출시하지 않는 절반이다.

토론 참여

← 블로그로 돌아가기