today keys : DNSSEC, RRset, DNSKEY, KSK, ZSK, RRSIG, DS, 트러스트 앵커, DO 비트
DNSSEC은 기존 DNS에 네 종류의 레코드를 추가하는 방식으로 동작합니다. 공개키를 담는 DNSKEY, 전자서명값인 RRSIG, 부모 존이 자식 키를 보증하는 DS, 그리고 이 모든 서명의 출발점이 되는 트러스트 앵커입니다.
이번 편에서는 앞으로 계속 반복해서 등장할 이 용어들을 한자리에 정리하겠습니다. 서명이 붙는 단위인 RRset부터 시작해 각 레코드가 무엇을 담고 어디에 위치하는지 확인하고, 마지막에 cloudflare.com의 실제 값으로 대조해보겠습니다.
1. 서명은 왜 요청해야만 보이는가 (DO 비트)
지난 편에서 +dnssec 옵션 유무에 따라 응답 구성이 달라지는 것을 확인했습니다. 이 차이부터 짚고 넘어가겠습니다.
DNSSEC이 적용된 존은 서명을 항상 가지고 있습니다. 다만 질의하는 쪽이 요청하지 않으면 응답에 실어 보내지 않을 뿐입니다. 이 요청 신호가 EDNS0 헤더에 들어가는 DO(DNSSEC OK) 비트입니다.
dig cloudflare.com A → DO=0 → 서명 없이 A 레코드만 응답
dig cloudflare.com A +dnssec → DO=1 → A 레코드 + RRSIG 함께 응답
이런 구조인 이유는 응답 크기 때문입니다. RRSIG는 서명값을 담고 있어 레코드 하나가 수백 바이트에 달합니다. 서명을 검증할 생각이 없는 클라이언트에게까지 이 데이터를 보내면 트래픽만 늘어납니다.
실제 운영 환경에서 DO 비트를 세우는 주체는 일반 클라이언트가 아니라 재귀 리졸버입니다. 클라이언트는 DO=0으로 묻고, 리졸버가 DO=1로 권한 서버들에게 질의해 서명을 받아 검증한 뒤, 클라이언트에게는 검증 결과만 알려주는 구조입니다. 우리가 +dnssec을 붙이는 것은 리졸버가 내부적으로 하는 일을 눈으로 보기 위한 것입니다.
서명이 없는 존에 DO=1로 질의하면
여기서 한 가지 궁금해지는 부분이 있습니다. DNSSEC이 적용되지 않은 존에 +dnssec을 붙여 질의하면 어떻게 될까요.
결론부터 말하면 정상 응답이 돌아옵니다. DO 비트는 "서명이 있으면 함께 보내달라"는 요청이지 "서명을 반드시 내놓으라"는 요구가 아니기 때문입니다. 서명이 없는 존은 평소와 동일하게 응답하고, 단지 RRSIG가 붙지 않을 뿐입니다.
zigi@ZIGI:~$ dig zigispace.net A +dnssec
; <<>> DiG 9.18.39-0ubuntu0.24.04.5-Ubuntu <<>> zigispace.net A +dnssec
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54240
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ← ad flag 없음
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096 ← flags : do 는 DO비트 인식 표기
;; QUESTION SECTION:
;zigispace.net. IN A
;; ANSWER SECTION:
zigispace.net. 10 IN A 211.249.222.34
;; Query time: 27 msec
;; SERVER: 10.255.255.254#53(10.255.255.254) (UDP)
;; WHEN: Fri Sep 11 17:20:04 KST 2026
;; MSG SIZE rcvd: 58
에러도 아니고 거부도 아닙니다. OPT 섹션의 flags: do는 서버가 DO 비트를 인식했다는 표시이고, ANSWER 섹션에는 RRSIG 없이 A 레코드만 돌아옵니다.
다만 검증을 수행하는 리졸버 입장에서는 이 상황이 단순히 "서명이 없으니 그냥 통과"로 끝나지 않습니다. 서명이 없다는 사실 자체를 별도로 증명받는 절차를 거치는데, 이 부분은 다음 편에서 체인 오브 트러스트와 함께 다루겠습니다.
2. RRset - 서명이 붙는 최소 단위
DNSSEC에서 서명은 레코드 한 줄마다 붙는 것이 아닙니다. RRset(Resource Record set) 이라는 묶음 단위로 붙습니다.
RRset은 이름(Owner Name)과 타입(Type)이 모두 같은 레코드들의 집합입니다. 예를 들어 아래와 같은 존이 있다고 하면,
example.com. A 10.10.10.10
example.com. A 10.10.10.11
www.example.com. A 10.10.10.20
www.example.com. AAAA 2001:db8::20
이 네 줄은 세 개의 RRset으로 나뉩니다.
| example.com A | 2줄 (.10, .11) | 이름·타입이 같아 한 묶음 |
| www.example.com A | 1줄 | 이름이 달라 별개 |
| www.example.com AAAA | 1줄 | 이름은 같지만 타입이 달라 별개 |
첫 번째 RRset은 레코드가 두 줄이지만 서명은 하나만 붙습니다. 반대로 www.example.com은 이름이 같아도 A와 AAAA가 서로 다른 RRset이므로 서명이 각각 하나씩, 총 두 개 붙습니다.
이 단위 개념이 중요한 이유는 검증할 때 그대로 적용되기 때문입니다. 리졸버는 레코드를 한 줄씩 확인하는 것이 아니라 RRset 전체를 묶어서 검증합니다. 따라서 공격자가 두 줄 중 한 줄만 조작해도 그 RRset 전체의 서명이 무효가 됩니다.
3. DNSKEY - 공개키를 담는 레코드
DNSSEC은 공개키 암호 방식을 사용합니다. 권한 서버가 개인키로 서명하고, 리졸버가 공개키로 검증하는 구조입니다.
여기서 리졸버가 사용할 공개키를 배포하는 통로가 필요한데, DNSSEC은 이것을 별도 채널이 아니라 DNS 레코드 자체로 해결합니다. 그 레코드가 DNSKEY입니다.
cloudflare.com. IN DNSKEY 257 3 13 mdsswUyr3DPW132mOi8V9xESWE8j...
cloudflare.com. IN DNSKEY 256 3 13 koPbw9wmYZ7ggcjnQ6ayHyhHaDNM...
필드 구성은 다음과 같습니다.
| Flags | 257 / 256 | 키의 역할 구분 (아래 설명) |
| Protocol | 3 | 고정값 (DNSSEC용) |
| Algorithm | 13 | 알고리즘 번호 (13=ECDSA P-256, 8=RSA/SHA-256) |
| Public Key | mdsswUyr... | Base64로 인코딩된 공개키 |
여기 담기는 것은 공개키뿐입니다. 개인키는 DNS 응답에 절대 실리지 않고 권한 서버 내부(또는 HSM)에만 보관됩니다. HTTPS에서 서버 인증서에 공개키만 들어 있고 개인키는 서버에만 있는 것과 동일한 구조입니다.
4. KSK와 ZSK - 키를 두 개로 나눈 이유
앞의 출력에서 DNSKEY가 두 줄이었던 것을 보셨을 텐데, 이는 존 하나가 역할이 다른 키 두 개를 운용하기 때문입니다. Flags 값으로 구분됩니다.
ZSK (Zone Signing Key) - Flags 256
존 안의 실제 레코드(A, MX, TXT 등)에 서명하는 키입니다. 레코드가 바뀔 때마다 사용되므로 노출 위험이 상대적으로 크고, 그만큼 자주 교체합니다(보통 1~3개월 주기).
KSK (Key Signing Key) - Flags 257
DNSKEY RRset, 즉 위 두 줄짜리 묶음 자체에만 서명하는 키입니다. 사용 빈도가 낮고 교체 주기가 깁니다(보통 1년 이상).
여기서 한 가지 짚고 넘어갈 점이 있습니다. KSK와 ZSK는 각각 공개키와 개인키로 이루어진 한 쌍(key pair)입니다. "KSK가 공개키인가 개인키인가"라는 질문은 성립하지 않습니다. 서명할 때는 개인키를 쓰고, DNSKEY 레코드로 게시되는 것은 공개키입니다.
왜 굳이 나눴는가
키를 하나만 쓴다면 더 단순했을 텐데 둘로 나눈 이유는 운영 편의성 때문입니다.
뒤에서 설명할 DS 레코드는 KSK의 해시값을 부모 존에 등록해둔 것입니다. 그런데 이 등록은 레지스트라를 통해 진행해야 하는 번거로운 절차입니다. 만약 키가 하나뿐이었다면 키를 교체할 때마다 매번 부모 존 등록을 갱신해야 합니다.
키를 둘로 나누면 이 문제가 해결됩니다.
ZSK 교체 → 존 내부에서만 처리 → 부모 존과 무관 → 자주 교체 가능
↓
KSK 교체 → DS 재등록 필요 → 번거로움 → 드물게 교체
자주 바꿔야 하는 키(ZSK)는 외부 절차 없이 교체하고, 외부 절차가 필요한 키(KSK)는 거의 안 바꾸도록 역할을 분리한 것입니다.
5. RRSIG - 전자서명을 담는 레코드
RRSIG는 특정 RRset에 대한 전자서명값을 담는 레코드입니다. 앞서 확인한 서명 대상 단위(RRset) 하나당 RRSIG 하나가 대응됩니다.
cloudflare.com. 266 IN RRSIG A 13 2 300 20261005000000 20260915000000 34505 cloudflare.com. DYYZ/bhHSAIlpvu...
한 줄로 보면 복잡해 보이지만 구성은 단순합니다.
| Type Covered | A | 어떤 타입의 RRset을 서명했는가 |
| Algorithm | 13 | 서명 알고리즘 |
| Labels | 2 | 소유자 이름의 레이블 수 |
| Original TTL | 300 | 서명 대상 RRset의 원래 TTL |
| Expiration | 20261005000000 | 서명 만료 시각 |
| Inception | 20260915000000 | 서명 유효 시작 시각 |
| Key Tag | 34505 | 어떤 키로 서명했는지 식별하는 번호 |
| Signer's Name | cloudflare.com. | 서명한 존의 이름 |
| Signature | DYYZ/bhHSAI... | 실제 서명값 |
마지막 Signature 필드가 실제 암호 연산의 결과물이고, 앞의 필드들은 리졸버가 검증할 때 필요한 부가 정보입니다.
여기서 주목할 것은 Expiration과 Inception입니다. DNSSEC 서명에는 유효기간이 있습니다. 서명 자체가 정상이어도 이 기간을 벗어나면 검증에 실패합니다. 이 때문에 존 관리자는 만료 전에 주기적으로 재서명해야 하고, 리졸버 쪽에서는 시스템 시간이 정확해야 합니다. NTP 동기화가 어긋난 서버에서 정상 도메인이 전부 조회되지 않는 장애가 이 지점에서 발생합니다.
Key Tag도 짚어둘 필요가 있습니다. 존에 DNSKEY가 여러 개(KSK, ZSK, 교체 중인 구키 등) 있을 때, 리졸버에게 "이 중 몇 번 키로 검증하라"고 알려주는 식별 번호입니다.
6. DS - 부모가 자식의 키를 보증하는 레코드
지금까지의 내용만으로는 아직 빈틈이 있습니다. 리졸버가 cloudflare.com의 DNSKEY를 받아왔다고 해도, 그 DNSKEY 자체가 진짜라는 보장이 없습니다. 공격자가 자기 키로 바꿔치기하고 자기 개인키로 서명을 붙이면 겉보기에는 완벽하게 맞아떨어집니다.
이 문제를 해결하는 것이 DS(Delegation Signer) 레코드입니다.
cloudflare.com. IN DS 2371 13 2 32996839A6D808AFE3EB4A795A0E6A7A39A76FC52FF228B22B76F6D63826F2B9
| Key Tag | 2371 | 어떤 KSK에 대한 것인지 |
| Algorithm | 13 | 해당 키의 알고리즘 |
| Digest Type | 2 | 해시 알고리즘 (2=SHA-256) |
| Digest | 32996839A6D8... | KSK 공개키의 해시값 |
DS의 핵심은 마지막 Digest 필드입니다. 자식 존 KSK 공개키를 해시한 값이 담깁니다.
그리고 가장 중요한 특성이 있습니다. DS 레코드는 자기 존이 아니라 부모 존에 위치합니다. cloudflare.com의 DS는 cloudflare.com 존이 아니라 com 존에 들어 있습니다. 그리고 com 존이 이 DS 레코드를 자기 키로 서명합니다.
즉 이런 관계가 성립합니다.
com 존이 cloudflare.com의 DS를 보유하고 서명
↓
리졸버가 cloudflare.com의 DNSKEY를 받아 해시 계산
↓
그 해시값이 com에 있던 DS와 일치하는지 대조
↓
일치하면 "이 DNSKEY는 부모가 보증한 진짜 키"로 확정
부모가 자식의 키를 보증하는 이 구조는 HTTPS 인증서 체인에서 상위 CA가 하위 CA 인증서에 서명해주는 것과 동일한 발상입니다.
7. 트러스트 앵커 - 루트만의 예외 처리
DS 구조를 따라 올라가면 한 가지 문제에 도달합니다.
cloudflare.com의 키는 → com이 보증 (DS)
↓
com의 키는 → 루트가 보증 (DS)
↓
루트의 키는 → 보증해줄 부모가 없음
루트 존에는 부모가 존재하지 않으므로 DS를 등록할 곳도 없습니다. 신뢰 사슬이 여기서 끊어집니다.
DNSSEC은 이 지점을 트러스트 앵커(Trust Anchor) 라는 예외 장치로 처리합니다. 루트의 KSK 공개키를 DNS가 아닌 별도의 안전한 경로로 미리 받아서 리졸버 설정에 직접 저장해두는 방식입니다. IANA가 이 값을 공식 배포하고, 각 리졸버 운영자가 이를 가져와 설정에 반영합니다.
이렇게 저장된 값은 리졸버가 검증 없이 신뢰하는 유일한 값이 됩니다. 나머지 모든 키는 이 앵커에서 출발해 DS를 타고 내려오며 검증되므로, 앵커 하나가 전체 신뢰 사슬의 출발점 역할을 합니다.
OS나 브라우저에 루트 CA 인증서가 미리 내장되어 있는 것과 정확히 같은 개념입니다. 최상위 신뢰만은 암호학적으로 증명하는 것이 아니라 사전에 안전하게 배포된 값을 믿는 것으로 처리합니다.
8. 용어 한눈에 정리
| RRset | 레코드 묶음 | - | 이름+타입이 같은 레코드 집합, 서명의 최소 단위 |
| DNSKEY | 레코드 타입 | 자기 존 | KSK·ZSK의 공개키를 게시 (257=KSK, 256=ZSK) |
| KSK | 키 쌍 | 자기 존 | DNSKEY RRset에만 서명, 해시가 DS로 등록됨 |
| ZSK | 키 쌍 | 자기 존 | 존 안의 실제 레코드에 서명, 자주 교체 |
| RRSIG | 레코드 타입 | 자기 존 | RRset 하나에 대한 서명값 + 유효기간 |
| DS | 레코드 타입 | 부모 존 | 자식 KSK 공개키의 해시값 |
| 트러스트 앵커 | 리졸버 설정값 | 리졸버 내부 | 루트 KSK 공개키, 신뢰 사슬의 시작점 |
📌 한 줄 요약: KSK가 ZSK를 서명하고, ZSK가 실제 레코드를 서명하며, KSK의 해시(DS)를 부모 존이 보증합니다. 그리고 부모가 없는 루트만 트러스트 앵커로 예외 처리됩니다.
9. 직접 확인해보기
지금까지 설명한 레코드들을 실제로 조회해보겠습니다.
DNSKEY 조회 - KSK와 ZSK 구분하기
dig cloudflare.com DNSKEY +short
두 줄이 출력되는데, 앞의 숫자가 257인 줄이 KSK, 256인 줄이 ZSK입니다. 세 번째 숫자(13)는 알고리즘 번호로, ECDSA P-256을 의미합니다.
DS 조회 - 부모 존에 등록된 해시값
dig cloudflare.com DS +short
출력된 마지막 긴 16진수 문자열이 KSK 공개키의 SHA-256 해시입니다. 이 값은 cloudflare.com이 아니라 com 존에서 응답한 것입니다. 아래 명령으로 어느 서버가 이 값을 돌려주는지 직접 확인할 수 있습니다.
dig cloudflare.com DS +trace
출력 중간에 gtld-servers.net 계열 서버(=com TLD 서버)가 DS 레코드를 응답하는 구간이 보입니다.
RRSIG 조회 - 유효기간 확인하기
dig cloudflare.com A +dnssec +multi
+multi 옵션을 붙이면 RRSIG 필드가 줄바꿈되어 출력되므로 읽기 편합니다. Expiration과 Inception 값을 보면 이 서명이 언제까지 유효한지 확인할 수 있습니다.
10. 참고 자료 (Reference Links)
- RFC 4034 - Resource Records for the DNS Security Extensions
- RFC 4035 - Protocol Modifications for the DNS Security Extensions
- IANA - DNSSEC Algorithm Numbers
- IANA - Root Zone Trust Anchors
※ 이전 포스팅
[DNSSEC ① - DNSSEC은 왜 필요한가]
다음 편에서는 이번에 정리한 레코드들이 어떻게 하나의 사슬로 연결되는지 다루겠습니다. 트러스트 앵커에서 출발해 루트, TLD를 거쳐 최종 도메인까지 신뢰가 전달되는 과정, 즉 체인 오브 트러스트(Chain of Trust)입니다. 앞서 미뤄둔 "서명 없는 존을 리졸버가 어떻게 판정하는가"도 여기서 함께 정리하겠습니다.