本文へスキップ
ecdsa.com
← すべての記事

DNSSEC の信頼の連鎖:DS、DNSKEY、RRSIG と障害の原因

検証を行うリゾルバーが信頼するのはルートの鍵だけで、そこから署名付きのポインターをたどって応答に到達します。DNSSEC の障害は、どれもこの連鎖のどこかが切れたものです。この記事では、dig と delv で実際の連鎖をたどったうえで、実験環境で意図的に連鎖を壊し、それぞれのミスでどのような症状が出るかを示します。

要点:DNSSEC は 2 種類のレコードでゾーン同士をつなぎます。親ゾーンの DS レコードは、子ゾーンの鍵署名鍵(DNSKEY、フラグ 257)のハッシュを保持しています。この鍵が子ゾーンの DNSKEY セットに署名し、ゾーン署名鍵(フラグ 256)が実際のレコードに RRSIG で署名します。検証を行うリゾルバーは、信頼しているルートの鍵から始めて、応答に至るまでのすべてのつながりを検証します。いずれかのつながりで検証に失敗すると、リゾルバーはデータの代わりに SERVFAIL を返します。連鎖は delv(「fully validated」)または dig +dnssec(ad フラグ)で確認し、親ゾーンの DS と子ゾーンの DNSKEY は dnssec-dsfromkey で比較します。データは存在するのに検証に失敗しているだけなのかどうかは、+cd で確かめられます。障害の大半は鍵のロールオーバーで起きます。具体的には、KSK を変更した後に更新されていない DS、期限切れになるまで放置された署名、キャッシュの有効期限が切れるより早く行われた鍵の差し替えです。

4 種類のレコード

レコード置かれる場所保持する内容
DNSKEYゾーン自体公開鍵。フラグ 257 は鍵署名鍵(KSK、「SEP」ビットが立っている)、256 はゾーン署名鍵(ZSK)を表す
RRSIG署名された各レコードセットの隣1 つの鍵による、1 つのレコードセット(RRset)への署名。署名開始時刻、有効期限、署名鍵の鍵タグを含む
DS親ゾーンの委任箇所子ゾーンの DNSKEY のいずれかのダイジェスト:鍵タグ、アルゴリズム、ダイジェストタイプ、ダイジェスト
NSEC / NSEC3ゾーン内名前またはタイプが存在しないことの署名付きの証明

この設計では、鍵を役割ごとに分けています。KSK が署名するのは DNSKEY RRset だけで、そのハッシュは親ゾーンに置かれます。そのため KSK を変更するには、親ゾーン側(レジストラまたはレジストリ)とのやり取りが必要です。ZSK はそれ以外のすべてに署名し、親ゾーンを関与させずに変更できます。両方の役割を兼ねる単一の鍵(CSK)を使うこともでき、BIND の dnssec-policy に組み込まれている default ポリシーはこの方式です(csk ... algorithm 13)。規則は変わらず、その鍵がフラグ 257 を持ち、すべてに署名するという点だけが異なります。

dig で実際の連鎖をたどる

2026年10月20日に、www.isc.org までの連鎖を順にたどりました(BIND 9.20.10 の dig を使用し、クエリは 1.1.1.1 に送信)。まずはルートです。ルートの DNSKEY セットには、ZSK が 1 つと KSK が 2 つあります。

dig . DNSKEY +multiline(抜粋)
.			2158 IN	DNSKEY 256 3 8 (
) ; ZSK; alg = RSASHA256 ; key id = 8763
.			2158 IN	DNSKEY 257 3 8 (
) ; KSK; alg = RSASHA256 ; key id = 20326
.			2158 IN	DNSKEY 257 3 8 (
) ; KSK; alg = RSASHA256 ; key id = 38696

鍵 20326 は KSK-2017、38696 は KSK-2024 です。ルート KSK のロールオーバーは、この出力を取得する 9 日前に行われました。ICANN は「2026年10月11日14時00分(UTC)、KSK-2024 は予定どおり DNS ルートゾーンへの署名を開始しました」と発表しています。ルートの DNSKEY セットに付いている署名からも、そのことを確認できます。

dig . DNSKEY +dnssec:ルートの鍵セットに対する署名
.			1587 IN	RRSIG DNSKEY 8 0 172800 (
20261101000000 20261011000000 38696 .

このセットに対する RRSIG はこれだけです。古い鍵は予定されている失効までは公開されたままですが、もう署名には使われていません。RFC 5011 の自動更新でもソフトウェアのアップデートでも KSK-2024 を取り込まなかったために KSK-2017 しか信頼していないリゾルバーは、もはや何も検証できません。

次は各委任です。親ゾーンの DS は、子ゾーンの KSK を鍵タグで指定しています。

dig org. DS、dig org. DNSKEY、dig isc.org. DS、dig isc.org. DNSKEY(抜粋)
org.			81000	IN	DS	26974 8 2 4FEDE294C53F438A158C41D39489CD78A86BEB0D8A0AEAFF14745C0D 16E1DE32
) ; ZSK; alg = RSASHA256 ; key id = 25488
) ; ZSK; alg = RSASHA256 ; key id = 11859
) ; ZSK; alg = RSASHA256 ; key id = 24060
) ; KSK; alg = RSASHA256 ; key id = 725
) ; KSK; alg = RSASHA256 ; key id = 26974
isc.org.		3600	IN	DS	7250 13 2 A30B3F78B6DDE9A4A9A2AD0C805518B4F49EC62E7D3F4531D33DE697 CDA01CB2
) ; ZSK; alg = ECDSAP256SHA256 ; key id = 27566
) ; KSK; alg = ECDSAP256SHA256 ; key id = 7250

ルートにある org. の DS は鍵 26974 を指しており、org. はこの鍵を KSK として公開しています。org. にある isc.org. の DS は、isc.org の KSK である鍵 7250 を指しています。下位へたどる途中でアルゴリズムが変わっている点に注目してください。org. は RSA(アルゴリズム 8)で、isc.org は ECDSA P-256(アルゴリズム 13)で署名しています。アルゴリズムはゾーンごとに選ぶものです。最後が、応答と、isc.org の ZSK である 27566 によるその署名です。

dig www.isc.org. A +dnssec +multiline
www.isc.org.		300 IN A 151.101.3.42
www.isc.org.		300 IN A 151.101.67.42
www.isc.org.		300 IN A 151.101.131.42
www.isc.org.		300 IN A 151.101.195.42
www.isc.org.		300 IN RRSIG A 13 3 300 (
				20261103074926 20261020065242 27566 isc.org.
				qz5Qb4BFyOw4PRXBJjeK61LrPSKo6G2Ma1aFwNwZ57x5
				bFIg/d3EoMHtXopLYU949QQtMjcQ8BWULk7a629tLA== )

RRSIG の各フィールドは、対象タイプ A、アルゴリズム 13、ラベル数 3、元の TTL 300、有効期限 2026年11月3日07時49分26秒(UTC)、署名開始時刻 2026年10月20日06時52分42秒(UTC)、鍵タグ 27566、署名者 isc.org. です。これらをすべて検証したリゾルバーは、AD(認証済みデータ)フラグを立てます。

dig www.isc.org A +dnssec @1.1.1.1
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

ad はリゾルバーによる主張にすぎず、その信頼性はリゾルバーまでの経路の信頼性を超えません。自分で検証するには delv を使います。組み込みのルートトラストアンカーを基準に、連鎖全体の追跡をローカルで行うツールです。+rtrace を付けると、取得の過程が表示されます。

delv @1.1.1.1 www.isc.org A +rtrace(BIND 9.20.10、抜粋)
;; fetch: www.isc.org/A
;; fetch: isc.org/DNSKEY
;; fetch: isc.org/DS
;; fetch: org/DNSKEY
;; fetch: org/DS
;; fetch: ./DNSKEY
; fully validated

古いガイドで勧められている dig +sigchase は廃止されました。dig 9.20 は dig: +sigchase option no longer supported と応答します。delv を使ってください。同じ追跡を DNS-over-HTTPS 経由でブラウザー内で行うには、DNSSEC インスペクターが使えます。このツールは、すべてのつながりを取得し、各 RRSIG を独自のコードで検証して、連鎖が切れている箇所を示します。

アルゴリズム 8、13、15

アルゴリズム番号は、DNSKEY、DS、RRSIG の各レコードに含まれます。RFC 9904(2025年11月)は RFC 8624 を置き換え、推奨事項を IANA レジストリ自体に移しました。同時に公開された RFC 9905 は、SHA-1 を使う 2 つのアルゴリズムによる署名を禁止しています。

番号アルゴリズム署名検証
5、7RSA/SHA-1禁止(MUST NOT)実装必須(MUST)、ただしリゾルバーの運用者は非対応として扱う
8RSA/SHA-256推奨(RECOMMENDED)、実装必須(MUST)実装必須(MUST)
10RSA/SHA-512非推奨(NOT RECOMMENDED)実装必須(MUST)
13ECDSA P-256/SHA-256推奨(RECOMMENDED)、実装必須(MUST)実装必須(MUST)
14ECDSA P-384/SHA-384任意(MAY)推奨(RECOMMENDED)
15Ed25519推奨(RECOMMENDED)推奨(RECOMMENDED)
16Ed448任意(MAY)推奨(RECOMMENDED)

DS のダイジェストには SHA-256(タイプ 2)を使うべきです。SHA-1(タイプ 1)は委任では「MUST NOT」ですが、バリデーターは引き続きこれをサポートしなければなりません。2026年10月20日に確認した 10 個のゾーンのうち、com.、net.、nl.、cloudflare.com、isc.org、ietf.org、nic.cz、sidn.nl は 13 で署名しており、org. と de. はルートと同じく 8 を使っています。同じゾーンをそれぞれのアルゴリズムで署名してみると、13 が広く使われている理由が分かります。

dnssec-keygen / dnssec-signzone 9.20.29:1 つのゾーン、3 つのアルゴリズム
algorithm 8  RSASHA256         ZSK public key 260 bytes, RRSIG signature 256 bytes
algorithm 13 ECDSAP256SHA256   ZSK public key  64 bytes, RRSIG signature  64 bytes
algorithm 15 ED25519           ZSK public key  32 bytes, RRSIG signature  64 bytes

署名が小さければ、応答を UDP のサイズ上限(現在一般的な EDNS バッファーサイズは 1232 バイト)内に収められるため、フラグメンテーションと TCP へのフォールバックを避けられます。アルゴリズム 15 は鍵が最も小さいものの、バリデーターによるサポートは MUST ではなく RECOMMENDED です。採用する前に、利用者が使っているリゾルバーを確認してください。

意図的に連鎖を壊す

実際のドメインを壊さずに障害を観察するため、以下の実験環境では Debian 13 上で BIND 9.20.29 のインスタンスを 2 つ動かします。1 つは、プライベートなルート .、lab.、shop.lab. の権威 DNS サーバーで、いずれのゾーンもアルゴリズム 13 で署名しています。もう 1 つは検証を行うリゾルバーで、そのトラストアンカーはこのプライベートなルートの DS だけです。鍵の生成と署名の手順には、BIND の通常のツールを使います。

実験環境の抜粋:鍵、署名、リゾルバーのトラストアンカー
keygen() { dnssec-keygen -q -K keys -a ECDSAP256SHA256 -L 3600 "$@"; }
for z in . lab shop.lab; do keygen -fKSK "$z" >/dev/null; keygen "$z" >/dev/null; done

# the parent includes the child's dsset file before signing
cat auth/db.lab.unsigned auth/dsset-shop.lab. > auth/db.lab
dnssec-signzone -q -S -K keys -d auth -o lab -f auth/db.lab.signed auth/db.lab

read -r _ _ _ tag alg dt d1 d2 < auth/dsset-.
echo "trust-anchors { . static-ds $tag $alg $dt \"$d1$d2\"; };" > anchors.conf

1. すべて正しい場合:lab. の DS は shop.lab. の KSK を指定しており、リゾルバーは ad を立て、delv は検証に成功します。

実験環境:ケース 1
DS in lab.: shop.lab. 48794 13 2
DNSKEY in shop.lab.: ZSK; alg = ECDSAP256SHA256 ; key id = 6251
DNSKEY in shop.lab.: KSK; alg = ECDSAP256SHA256 ; key id = 48794
$ dig @127.0.0.1 www.shop.lab A +dnssec
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
www.shop.lab.		5	IN	A	192.0.2.10
$ delv @127.0.0.2 -a anchors.conf www.shop.lab A
; fully validated
www.shop.lab.		5	IN	A	192.0.2.10

2. 子ゾーンで KSK を交換したが、親ゾーンの DS を更新していない場合:DNS ホスティングの移行後や、自動化された KSK ロールオーバーをレジストラ側で誰も完了させなかった場合に起こる、典型的な障害です。

実験環境:ケース 2
DS in lab.: shop.lab. 48794 13 2
DNSKEY in shop.lab.: KSK; alg = ECDSAP256SHA256 ; key id = 11943
DNSKEY in shop.lab.: ZSK; alg = ECDSAP256SHA256 ; key id = 6251
$ dig @127.0.0.1 www.shop.lab A +dnssec
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
$ dig @127.0.0.1 www.shop.lab A +dnssec +cd
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
;; flags: qr rd ra cd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
www.shop.lab.		5	IN	A	192.0.2.10
$ delv @127.0.0.2 -a anchors.conf www.shop.lab A
;; validating shop.lab/DNSKEY: no valid signature found (DS)
;; broken trust chain resolving 'www.shop.lab/A/IN': 127.0.0.2#53
;; resolution failed: broken trust chain

DS は依然として 48794 を指していますが、ゾーンの KSK は現在 11943 です。+cd(検証の無効化)を付けるとデータが返るため、ゾーン自体は稼働しており、検証だけが失敗していることが分かります。これが、DNSSEC の検証失敗とサービスそのものの停止を見分ける最も手早い方法です。

3. 署名の期限切れ:DS は修正済みですが、ゾーンは一度署名されたきり再署名されていません。ここでは実験環境で、署名開始時刻を 10 日前、有効期限を 1 時間前にして署名します。

実験環境:ケース 3
RRSIG inception 20261010131152 expiration 20261020121152
$ dig @127.0.0.1 www.shop.lab A +dnssec
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
; EDE: 7 (Signature Expired): (shop.lab/DNSKEY)
$ delv @127.0.0.2 -a anchors.conf www.shop.lab A
;; validating shop.lab/DNSKEY: no valid signature found (DS)
;; broken trust chain resolving 'www.shop.lab/A/IN': 127.0.0.2#53
;; resolution failed: broken trust chain

BIND は、原因を示す拡張 DNS エラー(Extended DNS Error、RFC 8914)を付加します。ここでは EDE: 7 (Signature Expired) です。dnssec-signzone による静的な署名では、署名の有効期間はデフォルトで 30 日です(実験環境での最初の署名では、署名開始時刻が 20261020120639、有効期限が 20261119120639 でした)。そのため、再署名を行う cron ジョブが止まると、1 か月後にまさにこの状態になります。代わりに、DNS サーバーに署名と再署名を自動で行わせてください。BIND なら dnssec-policy を使います。そのデフォルトでは、有効期間 14 日の署名を 5 日ごとに更新します。Knot や PowerDNS にも同等の機能があります。

4. ZSK を一度に差し替えた場合:ゾーンが ZSK を交換し、すぐに新しい鍵ですべてを再署名します。古い DNSKEY セット(TTL 3600)をキャッシュしているリゾルバーは、自分が持っていない鍵で署名された A レコードを受け取ることになります。

実験環境:ケース 4
$ dig @127.0.0.1 www.shop.lab A +dnssec
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
www.shop.lab.		5	IN	A	192.0.2.10
resolver cache: DNSKEY flags 256 TTL 3600
resolver cache: DNSKEY flags 257 TTL 3600
(6 s later: the A record's 5 s TTL has expired, the cached DNSKEY set's 3600 s TTL has not)
$ dig @127.0.0.1 www.shop.lab A +dnssec
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
(resolver restarted, cache empty)
$ dig @127.0.0.1 www.shop.lab A +dnssec
;; ->>HEADER<<- opcode: QUERY, status: NOERROR
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
www.shop.lab.		5	IN	A	192.0.2.10

ゾーン自体は有効で、キャッシュが空のリゾルバーなら検証に成功します。しかし、古い鍵セットをキャッシュしたリゾルバーはすべて、そのキャッシュエントリの有効期限が切れるまで検証に失敗します。この障害は断続的で、リゾルバーによって起きたり起きなかったりするため、最もデバッグしにくい種類の障害です。

障害を起こさないロールオーバー

RFC 6781 は、安全な手順を説明しています。そのすべてに共通する原則は、新旧のレコードがどのように混在してキャッシュされていても、すべてのリゾルバーが検証できなければならない、というものです。

  • ZSK の事前公開法:古い ZSK で署名を続けたまま、新しい ZSK を DNSKEY セットに追加します。すべてのキャッシュが両方の鍵を持つよう、少なくとも DNSKEY の TTL の間は待ちます。署名を新しい鍵に切り替えます。署名済みデータの TTL のうち最も長いものが経過して、古い署名がキャッシュから消えるまで待ちます。その後、古い鍵を削除します。
  • KSK の二重署名法:新しい KSK を追加し、DNSKEY セットに両方の鍵で署名します。新しい DS を親ゾーンに提出します。親ゾーンがそれを公開し、古い DS がキャッシュで期限切れになる(DS の TTL、多くの場合 1 時間か 1 日)まで待ちます。そこで初めて、古い KSK を削除します。
  • アルゴリズムのロールオーバー(例:8 から 13 へ):新しい DS が現れる前に、新しいアルゴリズムの新しい鍵ですべてに署名しておかなければなりません。また、古い DS がなくなるまで、古いアルゴリズムでの署名を続けなければなりません。完全な手順は RFC 6781 §4.1.4 に記載されています。
  • 親ゾーン側を自動化する:CDS レコードと CDNSKEY レコード(RFC 8078)を使うと、対応している親ゾーンは新しい DS を子ゾーンから取り込めます。これがない場合、DS の手順はレジストラでの手作業になり、ロールオーバーが滞るのはまさにこの段階です。
  • DNS プロバイダーの移行:新旧のプロバイダーが連携する複数署名者(multi-signer)方式で移行するか、まずレジストラで DS を削除し、その TTL が経過するのを待ってから、新しいプロバイダーの鍵で DNSSEC を再び有効にします。古い DS を残したまま NS レコードを移すと、本番環境でケース 2 が起きることになります。

BIND の dnssec-policy や、Knot と PowerDNS の鍵管理機能は、こうしたタイミングを自動で処理します。よくある失敗の原因はツールではなく親ゾーン側の手順、つまりレジストラの Web フォームに貼り付けなければならない DS です。

SERVFAIL の診断

  1. DNSSEC が原因か:dig name +cd ではデータが返るのに dig name では SERVFAIL になる場合は、検証の失敗です。EDE の行を探してください。6(DNSSEC Bogus)、7(Signature Expired)、8(Signature Not Yet Valid)、9(DNSKEY Missing)、10(RRSIGs Missing)があります。
  2. どのつながりか:delv name +rtrace を実行し、最初に失敗している段階を読み取ります。
  3. DS と DNSKEY の比較:子ゾーンの鍵から DS を計算し、親ゾーンにある DS と比較します。
親ゾーンの DS と、子ゾーンの DNSKEY から計算した DS の比較(bind9-utils 9.20.29)
$ dig +short DS isc.org @1.1.1.1
7250 13 2 A30B3F78B6DDE9A4A9A2AD0C805518B4F49EC62E7D3F4531D33DE697 CDA01CB2
$ dig +cd +noall +answer DNSKEY isc.org @1.1.1.1 | dnssec-dsfromkey -2 -f - isc.org
isc.org. IN DS 7250 13 2 A30B3F78B6DDE9A4A9A2AD0C805518B4F49EC62E7D3F4531D33DE697CDA01CB2
$ dig +short DS dnssec-failed.org @1.1.1.1
42069 13 2 62726F6B656E20636861696E206F662074727573742073656E642068 656C7021
$ dig +cd +noall +answer DNSKEY dnssec-failed.org @1.1.1.1 | dnssec-dsfromkey -2 -f - dnssec-failed.org
dnssec-failed.org. IN DS 50719 13 2 D602C971A02AF89B7661B99D6DD1B9E2EC876617631D04F633902A42CD4D6BBD

isc.org は一致しています。dnssec-failed.org は、Comcast のネームサーバー(dns101.comcast.net など)が提供している、意図的に壊されたテスト用ドメインです。その DS は存在しない鍵 42069 を指定しており、ダイジェストは ASCII で「broken chain of trust send help!」と読めます。当然、パブリックリゾルバーはこのドメインを拒否します。

dig @1.1.1.1 dnssec-failed.org A
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 59477
; EDE: 9 (DNSKEY Missing): (no SEP matching the DS found for dnssec-failed.org.)
  1. 署名の時刻:RRSIG の署名開始時刻と有効期限を、現在の UTC 時刻と比較します。署名者またはバリデーターの時計がずれていると、「まだ有効ではない」または「期限切れ」という結果になります。
  2. リゾルバーによって異なる失敗:鍵の変更後に起きるこうした失敗は、キャッシュが原因であることを示しています(ケース 4)。DNSKEY セットと DS セットの TTL、そして変更した時刻を確認してください。

DNSSEC で可能になること

正しく機能する信頼の連鎖は、可用性だけでなく真正性も必要とするレコードの土台です。DANE/TLSA は DNS で TLS の鍵を固定する仕組みで、メールサーバー間の SMTP で使われています。TLSA レコード生成ツールで、こうしたレコードを作成できます。CAA レコードは CA が確認するもので、2026年3月15日以降、CA/Browser Forum のベースライン要件は、ゾーンが署名されている場合には CAA の参照で DNSSEC の検証を行うことを求めています。そのため、連鎖が壊れていると、証明書の発行が妨げられることもあります。

出典

実環境へのクエリは、2026年10月20日に nicolaka/netshoot:v0.14(BIND 9.20.10 の dig と delv)で実行しました。実験環境と dnssec-dsfromkey は、Debian が提供する BIND 9.20.29 を使い、debian:trixie-20261005-slim で実行しました。