DNSSEC Part 6 : DNSSEC Flow 확인
today keys : DNSSEC, DO 비트, 위임 응답, DNSKEY 질의, RRSIG, DS, AD 플래그, 캐시
지금까지 DNSSEC의 레코드와 검증 원리, 생성 절차를 나눠서 살펴봤습니다. 이번 편에서는 이 내용을 하나의 조회 과정에 전부 대입해봅니다. 클라이언트가 도메인 하나를 물었을 때 리졸버가 어떤 서버에 무엇을 질의하고, 어떤 값을 받아 무엇과 대조하는지를 순서대로 추적합니다.
조회 한 번에 여덟 번의 패킷 교환이 일어납니다. 각 단계마다 출발지와 목적지, 주고받는 값을 모두 표기했으니 앞 편들에서 개별적으로 다룬 개념들이 어느 지점에서 쓰이는지 확인하면서 읽으시면 됩니다.
1. 추적 대상과 표기 방법
www.cloudflare.com의 A 레코드를 조회하는 상황을 가정합니다. 등장하는 구성요소는 네 개입니다.
| 구성요소 | 주소 (예시) | 역할 |
| 클라이언트 | 192.168.0.10 |
조회를 시작하는 PC |
| 재귀 리졸버 | 10.10.10.53 |
검증을 수행하는 사내 DNS |
| 루트 서버 | 198.41.0.4 |
a.root-servers.net |
| com 권한 서버 | 192.5.6.30 |
a.gtld-servers.net |
| cloudflare.com 권한 서버 | - | ns3.cloudflare.com 등 |
각 단계를 Q-0부터 Q-7까지 번호로 표기하겠습니다.
캐시가 완전히 비어 있는 상태를 기준으로 하며, 실제 운영 환경에서는 상당 부분이 캐시에서 처리됩니다.
2. Q-0 : 클라이언트 → 리졸버
출발지 : 192.168.0.10:52341 (클라이언트)
목적지 : 10.10.10.53:53 (재귀 리졸버)
QNAME : www.cloudflare.com
QTYPE : A
플래그 : rd=1 (재귀 요청)
EDNS0 : DO=0
여기서 눈여겨볼 것은 DO 비트가 0이라는 점입니다.
클라이언트는 서명 데이터를 요구하지 않습니다.
검증은 리졸버에게 맡기고 결과만 받으면 되기 때문입니다.
rd=1은 "네가 대신 찾아와 달라"는 재귀 요청입니다.
이 플래그 하나로 리졸버는 아래의 모든 단계를 대신 수행하게 됩니다.
3. Q-1 · Q-2 : 루트 구간
리졸버는 캐시가 비어 있으므로 루트부터 시작합니다.
Q-1 : A 레코드 질의
출발지 : 10.10.10.53 (리졸버)
목적지 : 198.41.0.4:53 (루트 서버)
QNAME : www.cloudflare.com
QTYPE : A
플래그 : rd=0 ← 권한 서버에는 재귀를 요청하지 않음
EDNS0 : DO=1 ← 이제부터는 서명 데이터도 요구
루트는 이 도메인의 답을 모릅니다. 대신 "com 서버에게 물어보라"는 위임 응답을 돌려줍니다.
AUTHORITY:
com. 172800 IN NS a.gtld-servers.net.
com. 86400 IN DS 19718 13 2 8ACBB0CD28F41250A80A491389424D34...
com. 86400 IN RRSIG DS 8 1 86400 ... <루트 ZSK 태그> . <서명값>
ADDITIONAL:
a.gtld-servers.net. A 192.5.6.30
받은 값은 세 종류입니다. 다음에 물어볼 곳(NS), com 존의 KSK 해시(DS), 그리고 그 DS에 대한 서명(RRSIG)입니다.
여기서 두 가지를 짚어둘 필요가 있습니다.
첫째, DS는 com이라는 존의 것이지 a.gtld-servers.net이라는 서버의 것이 아닙니다. 위임 대상 존과 그 존을 서비스하는 서버 이름은 별개입니다.
둘째, NS와 glue 레코드에는 RRSIG가 없습니다. 위임 참조 정보는 부모가 소유한 데이터가 아니라 서명 대상이 아니기 때문입니다. DNSSEC이 보증하는 것은 도달한 서버가 응답하는 데이터이지 위임 경로 자체가 아닙니다.
이 시점에는 아직 아무것도 검증할 수 없습니다. RRSIG를 풀어볼 공개키가 없기 때문입니다.
Q-2 : DNSKEY 질의
출발지 : 10.10.10.53 (리졸버)
목적지 : 198.41.0.4:53 (루트 서버)
QNAME : .
QTYPE : DNSKEY
EDNS0 : DO=1
ANSWER:
. 172800 IN DNSKEY 257 3 8 AwEAAaz/tAm8y... ← KSK
. 172800 IN DNSKEY 256 3 8 AwEAAbXY2mNg... ← ZSK
. 172800 IN RRSIG DNSKEY 8 0 172800 ... <루트 KSK 태그> .
이제 검증에 필요한 재료가 모였습니다. 3부에서 다룬 3단계가 여기서 수행됩니다.
① 받은 KSK를 해시해 트러스트 앵커의 Digest와 대조 → 루트 KSK 확정
↓
② KSK 공개키로 DNSKEY RRSIG 검증 → 같은 RRset의 ZSK까지 신뢰 확보
↓
③ ZSK 공개키로 Q-1에서 받아둔 com DS의 RRSIG 검증 → com DS 확정
③단계가 중요합니다. Q-1에서 받은 DS는 그때는 검증할 수 없어 보류 상태였는데, Q-2에서 ZSK를 확보한 뒤에야 비로소 검증됩니다. 받은 시점과 검증 시점이 다르다는 점이 패킷 단위로 보면 분명해집니다.
4. 왜 DNSKEY를 따로 물어야 하는가
Q-2를 보면서 드는 의문이 있습니다. 어차피 검증에 필요하다면 Q-1 응답에 DNSKEY도 함께 보내주면 되지 않을까요.
이유는 두 가지입니다.
응답 크기 — DNSKEY RRset은 KSK와 ZSK 공개키를 모두 담고 있어 크기가 큽니다. 모든 응답에 끼워 보내면 트래픽이 크게 늘어납니다.
재사용 가능성 — 한 존의 DNSKEY를 한 번 받아두면 그 존의 모든 RRSIG 검증에 재사용할 수 있습니다. 매번 보낼 이유가 없습니다.
그래서 DNSKEY는 리졸버가 필요할 때만 별도로 요청합니다. 판단 기준은 RRSIG의 Key Tag 필드입니다.
RRSIG 수신 → Key Tag 확인 → "이 태그의 키로 검증하라"
↓
캐시에 해당 존의 DNSKEY가 있는가?
↓ 있음 → 바로 검증
↓ 없음 → DNSKEY 질의 발송 (Q-2, Q-4, Q-6이 이 경우)
같은 도메인을 두 번째로 조회하면 이 질의들이 사라집니다. DNSKEY가 이미 캐시에 있기 때문입니다. 실제 환경에서 매 조회마다 여덟 번의 패킷이 오가지는 않는 이유입니다.
5. Q-3 · Q-4 : com 구간
Q-3 : A 레코드 질의
출발지 : 10.10.10.53 (리졸버)
목적지 : 192.5.6.30:53 (com 권한 서버)
QNAME : www.cloudflare.com
QTYPE : A
EDNS0 : DO=1
AUTHORITY:
cloudflare.com. 172800 IN NS ns3.cloudflare.com.
cloudflare.com. 86400 IN DS 2371 13 2 32996839A6D808AFE3EB4A79...
cloudflare.com. 86400 IN RRSIG DS 13 2 86400 ... 41446 com. <서명값>
ADDITIONAL:
ns3.cloudflare.com. A ...
구조가 Q-1과 동일합니다. 위임 정보와 자식 존의 DS, 그리고 그 서명입니다.
RRSIG의 마지막 두 필드를 보시면 키 태그가 41446이고 서명자 이름이 com.입니다.
DS 값 자체는 Cloudflare가 레지스트라를 통해 제출한 것이지만 그 DS에 서명한 주체는 부모인 com이라는 사실이 이 두 필드에 드러납니다.
Q-4 : DNSKEY 질의
출발지 : 10.10.10.53 (리졸버)
목적지 : 192.5.6.30:53 (com 권한 서버)
QNAME : com.
QTYPE : DNSKEY
EDNS0 : DO=1
ANSWER:
com. 86400 IN DNSKEY 257 3 13 tx8EZRAd2+K/DJRV0S+hbBzaRPS/... ← KSK
com. 86400 IN DNSKEY 256 3 13 koPbw9wmYZ7ggcjnQ6ayHyhHaDNM... ← ZSK (41446)
com. 86400 IN RRSIG DNSKEY 13 1 86400 ... <com KSK 태그> com.
루트 구간과 완전히 같은 3단계가 반복됩니다. 다른 것은 대조 기준뿐입니다.
① 받은 com KSK를 해시해 Q-1의 DS(8ACBB0CD...)와 대조 → com KSK 확정
↓
② com KSK로 DNSKEY RRSIG 검증 → com ZSK(41446) 신뢰 확보
↓
③ com ZSK로 Q-3의 cloudflare.com DS RRSIG 검증 → DS 확정
루트 구간에서는 ①의 기준이 트러스트 앵커였고, 여기서는 앞 구간에서 검증을 마친 DS입니다.
이것이 신뢰가 계층을 따라 전달된다는 말의 실체입니다.
6. Q-5 · Q-6 : 권한 서버 구간
Q-5 : A 레코드 질의
출발지 : 10.10.10.53 (리졸버)
목적지 : ns3.cloudflare.com (권한 서버)
QNAME : www.cloudflare.com
QTYPE : A
EDNS0 : DO=1
이번에는 위임이 아니라 실제 답이 돌아옵니다.
ANSWER:
www.cloudflare.com. 300 IN A 104.16.132.229
www.cloudflare.com. 300 IN A 104.16.133.229
www.cloudflare.com. 300 IN RRSIG A 13 3 300 (
20261005000000 20260915000000
34505 cloudflare.com. <서명값> )
A 레코드가 두 줄인데 RRSIG는 하나뿐입니다. 2부에서 다룬 RRset 단위 서명이 실물로 드러나는 지점입니다. 두 줄이 이름도 타입도 같아 하나의 RRset을 이루므로 서명도 하나만 붙습니다.
이 구조 때문에 공격자가 두 줄 중 한 줄만 바꿔도 RRset 전체의 서명이 무효가 됩니다.
Q-6 : DNSKEY 질의
출발지 : 10.10.10.53 (리졸버)
목적지 : ns3.cloudflare.com (권한 서버)
QNAME : cloudflare.com.
QTYPE : DNSKEY
EDNS0 : DO=1
ANSWER:
cloudflare.com. IN DNSKEY 257 3 13 mdsswUyr3DPW132mOi8V9xES... ← KSK (2371)
cloudflare.com. IN DNSKEY 256 3 13 koPbw9wmYZ7ggcjnQ6ayHyhH... ← ZSK (34505)
cloudflare.com. IN RRSIG DNSKEY 13 2 ... 2371 cloudflare.com.
마지막 3단계입니다. 앞의 두 구간과 구조는 같지만 ③의 대상이 다릅니다.
① 받은 KSK를 해시해 Q-3의 DS(32996839...)와 대조 → KSK 확정
↓
② KSK로 DNSKEY RRSIG 검증 → ZSK(34505) 신뢰 확보
↓
③ ZSK로 Q-5의 A 레코드 RRSIG 검증 → A 레코드 확정
중간 구간에서는 ③의 결과가 "다음 계층의 DS"였지만, 마지막 구간에서는 실제로 조회하려던 A 레코드가 나옵니다.
동시에 RRSIG의 유효기간(20260915000000 ~ 20261005000000)이 현재 시각을 포함하는지도 확인합니다.
서명이 정상이어도 이 조건을 만족하지 않으면 검증은 실패합니다.
7. Q-7 : 리졸버 → 클라이언트
출발지 : 10.10.10.53:53 (리졸버)
목적지 : 192.168.0.10:52341 (클라이언트)
플래그 : qr=1 rd=1 ra=1 ad=1
ANSWER:
www.cloudflare.com. 300 IN A 104.16.132.229
www.cloudflare.com. 300 IN A 104.16.133.229
클라이언트가 DO=0으로 물었으므로 RRSIG는 제외하고 A 레코드만 전달합니다.
클라이언트는 서명을 받아봐야 검증할 능력도 의사도 없기 때문입니다.
대신 ad 플래그를 세워 "내가 사슬 전체를 검증했고 이 데이터는 진짜다"를 알립니다.
📌 한 줄 요약: 클라이언트가 보내고 받는 것은 각각 한 번뿐이지만, 그 사이에서 리졸버는 세 계층을 오가며 여섯 번의 질의를 수행하고 아홉 번의 검증 연산을 거칩니다.
8. 전체 흐름 요약
| 단계 | 출발지 | 목적지 | 응답 값 |
| Q-0 | 클라이언트 | 리졸버 | - |
| Q-1 | 리졸버 | 루트 | com NS · DS · RRSIG |
| Q-2 | 리졸버 | 루트 | 루트 DNSKEY (KSK+ZSK) |
| Q-3 | 리졸버 | com | cloudflare.com NS · DS · RRSIG |
| Q-4 | 리졸버 | com | com DNSKEY |
| Q-5 | 리졸버 | 권한 서버 | A 레코드 + RRSIG |
| Q-6 | 리졸버 | 권한 서버 | cloudflare.com DNSKEY |
| Q-7 | 리졸버 | 클라이언트 | A 레코드 + ad 플래그 |
Q-0과 Q-7을 제외한 모든 단계에서 출발지는 항상 리졸버입니다.
클라이언트는 처음 한 번 묻고 마지막에 결과만 받을 뿐, 재귀 조회와 검증은 전부 리졸버가 단독으로 수행합니다.
패턴을 한 줄로 정리하면 이렇습니다.
부모에게서 DS를 받고 → 자식에게 DNSKEY를 물어 해시로 대조하고 → 그 키로 하위 서명을 검증한다
이 한 사이클이 계층 수만큼 반복될 뿐이며, 시작점만 트러스트 앵커이고 끝점만 실제 레코드입니다.
9. 직접 확인해보기
전 과정을 한 번에 재현하기
dig www.cloudflare.com A +trace +dnssec
+trace는 리졸버가 수행하는 재귀 조회를 단계별로 재현해 보여줍니다. 출력을 위에서부터 읽으면 루트 → com → cloudflare.com 순으로 내려가면서 각 위임 지점의 DS와 RRSIG가 차례로 나타납니다. 이번 편에서 다룬 Q-1, Q-3, Q-5가 그대로 보입니다.
igi@ZIGI:~$ dig @8.8.8.8 www.cloudflare.com A +trace +dnssec
; <<>> DiG 9.18.39-0ubuntu0.24.04.5-Ubuntu <<>> @8.8.8.8 www.cloudflare.com A +trace +dnssec
; (1 server found)
;; global options: +cmd
. 219896 IN NS b.root-servers.net.
. 219896 IN NS g.root-servers.net.
. 219896 IN NS e.root-servers.net.
. 219896 IN NS c.root-servers.net.
. 219896 IN NS a.root-servers.net.
. 219896 IN NS d.root-servers.net.
. 219896 IN NS i.root-servers.net.
. 219896 IN NS m.root-servers.net.
. 219896 IN NS j.root-servers.net.
. 219896 IN NS k.root-servers.net.
. 219896 IN NS h.root-servers.net.
. 219896 IN NS f.root-servers.net.
. 219896 IN NS l.root-servers.net.
. 367822 IN RRSIG NS 8 0 518400 20260926050000 20260913040000 57780 . Q37W5pdRJYTMeMFzvbqd06nQFMPglEiLEBYxAO40Eca+b9L0KMgxRaJx aKHJjNfuwVi2R1EvZTw4v0U6G8A/WKliLTPe1N+vRBnDRi6TXTAF2LpA 9WbEQI+XIbn/OFA7OQlg5kOoA7IDrbz7yuGETUyDzXAM/FCW5WcS83/V izZh3Q7h8vHsf8tf+EIMcsS5dfCmjFvf0tohkT40Np2GHNjy4P57gV1M wVClVlRXhuBdiwcllpJyvGHU2LFkt5v2wDlPkmK4IqRjTaSMDEFEhyeW zL/BunXlH38swBEcJ2xK0dMzqz1CkCk2HYn1uIkpTeKPOLUue1J/79sR 2JyFTw==
;; Received 1097 bytes from 8.8.8.8#53(8.8.8.8) in 32 ms
www.cloudflare.com. 221 IN A 104.16.123.96
www.cloudflare.com. 221 IN A 104.16.124.96
www.cloudflare.com. 221 IN RRSIG A 13 3 300 20260915153017 20260913133017 34505 www.cloudflare.com. 8eQBYpwKnKH+4Hu2LfGDJjZrGdgWehNmyS9rZQcRc8BUgrYRel9pSuk+ DzFJGVpOVlYEgaV6X+WJjA7LlyGS+g==
;; Received 193 bytes from 199.7.91.13#53(d.root-servers.net) in 56 ms
ADDITIONAL 섹션의 glue 레코드까지 보려면 옵션을 추가합니다.
dig www.cloudflare.com A +trace +additional +dnssec
DNSKEY 질의를 따로 확인하기
+trace 출력에는 DNSKEY 질의가 섞여 보이지 않을 수 있습니다. 각 계층의 DNSKEY를 직접 물어보면 Q-2, Q-4, Q-6이 무엇을 받아오는지 확인할 수 있습니다.
dig . DNSKEY +dnssec
dig com DNSKEY +dnssec
dig cloudflare.com DNSKEY +dnssec
세 응답 모두 257과 256 두 줄에 RRSIG 하나가 붙어 있는, 동일한 구조입니다.
검증 순서를 그대로 보기
delv +vtrace www.cloudflare.com A
delv는 BIND에 포함된 검증 전용 도구로, +vtrace를 붙이면 트러스트 앵커 로드부터 각 계층의 DS 대조와 RRSIG 검증까지 순서대로 출력됩니다. 이번 편의 3장·5장·6장에서 설명한 3단계가 실제로 반복되는 모습을 확인할 수 있습니다.
사내망이나 WSL 환경에서 검색 도메인이 붙어 실패하는 경우가 있습니다. 그럴 때는 도메인 끝에 점을 붙이거나 리졸버를 직접 지정하면 됩니다.
delv @1.1.1.1 www.cloudflare.com A
캐시 효과 확인하기
같은 질의를 두 번 연속 실행하면 응답 시간이 크게 줄어듭니다.
dig www.cloudflare.com A +dnssec @1.1.1.1
dig www.cloudflare.com A +dnssec @1.1.1.1
두 번째 실행에서는 DNSKEY와 DS가 모두 캐시에 있어 검증만 수행하거나, 검증 결과까지 캐시된 상태로 바로 응답됩니다.
10. 참고 자료 (Reference Links)
- RFC 4035 - Protocol Modifications for the DNS Security Extensions
- RFC 6840 - Clarifications and Implementation Notes for DNSSEC
- DNSViz - DNSSEC 체인 시각화 도구
- ISC - BIND DNSSEC Guide
