DNSの切り分けはAレコードから権威サーバーとキャッシュへ進む
目次
DNSの失敗は、ブラウザのエラーとして出てくる頃には原因が散っている。
Aレコードが間違っていることもあるし、権威DNSには正しい値が入っているのに再帰リゾルバのキャッシュだけ古いこともある。
CDNを挟む構成なら、名前は引けるのにプロキシ設定や証明書側で止まる。
Gbemisola Eshoの「Understanding DNS」 は、DNSを電話帳にたとえつつ、セキュリティの入口、DNSトンネリング、キャッシュポイズニング、性能と冗長化まで広げている。
ただ、運用で詰まるのは「DNSが何者か」より、どの段で答えが変わっているかを切り分けるところだった。
ブラウザの手前で答えが何度も変わる
名前解決は、ブラウザから権威DNSへ一直線に飛ぶわけではない。
OSのキャッシュ、ルーター、ISPやパブリックDNSの再帰リゾルバ、TLD、権威DNSが順に絡む。
flowchart TD
A[ブラウザ] --> B[OS / ルーターのキャッシュ]
B --> C[再帰リゾルバ]
C --> D[ルートDNS]
D --> E[TLD DNS]
E --> F[権威DNS]
F --> C
C --> B
B --> A
たとえば www.example.com のAレコードをCloudflareで変えた直後、Cloudflareの権威DNSは新しいIPを返していても、手元のPCは古い答えを持っていることがある。
この状態でブラウザだけを見ても、変更が反映していないのか、キャッシュが残っているのか、CDN側が別の応答を返しているのか分からない。
dig で見る場所を分ける。
# 手元で使っているリゾルバ越しの答え
dig www.example.com A
# Cloudflare Public DNS越しの答え
dig @1.1.1.1 www.example.com A
# Google Public DNS越しの答え
dig @8.8.8.8 www.example.com A
# 委任をたどって権威DNSまで見る
dig +trace www.example.com A
dig +trace は、BINDのマニュアルでも説明されている通り、ルートからTLD、権威DNSまで委任経路をたどる。
ここで正しい答えが返るなら、ドメイン側の設定はだいたい通っている。
一方、@1.1.1.1 や @8.8.8.8 で古い答えが返るなら、再帰リゾルバのキャッシュ待ちになる。
TTLは反映待ち時間そのものではない
TTLは「この答えを何秒キャッシュしてよいか」という上限だ。
DNS管理画面でTTLを300秒にしたからといって、世界中が5分で同じ答えになるわけではない。
RFC 1034では、各リソースレコードにTTLがあり、キャッシュに保持できる時間として扱われる。
権威DNSが返すTTLは元の値だが、再帰リゾルバ越しに見るTTLは残り時間として減っていく。
同じリゾルバへ数十秒あけて dig @1.1.1.1 www.example.com A を打ち、TTLだけが減るなら、いま見ているのは権威DNSではなくキャッシュだ。
古いレコードを長いTTLで配っていたなら、その答えを受け取った再帰リゾルバは期限まで持つ。
変更後のTTLを短くしても、すでに配られた古い答えの寿命は巻き戻らない。
切り替え前にTTLを短くしておき、古いTTLが抜けてからAレコードやCNAMEを変えると、切り戻しも含めて扱いやすくなる。
存在しない名前もキャッシュされる。
新しいサブドメインを作る前に new.example.com を引いてNXDOMAINを受け取っていた場合、その「存在しない」という答えも一定時間残る。
RFC 2308はこれをネガティブキャッシュとして整理していて、実務ではNXDOMAIN応答のAUTHORITY SECTIONに出るSOAとTTLを見る。
dig @1.1.1.1 new.example.com A
dig @1.1.1.1 example.com SOA
「Aレコードを作ったのにまだ引けない」場合、権威DNSを直接叩くと新しいAが返り、再帰リゾルバ越しではNXDOMAINが返ることがある。
これは設定ミスではなく、作成前に問い合わせた否定応答が残っているだけ、という切り分けになる。
V2Ray(WebSocket+TLS)サーバーの構築メモ では、Cloudflareでサブドメインを作り、DNS反映を待ってからインストーラーを動かす流れだった。
この手順で止まったときは、インストーラーの失敗だけを見るより、権威DNS、パブリックリゾルバ、手元のOSキャッシュを分けて見るほうが早い。
Aレコードだけ見てもメールとCDNは分からない
Webサイトの疎通確認だとAやAAAAに目が行く。
でも同じドメインでメール、CDN、証明書、外部SaaSを使うと、別のレコードが原因になる。
| レコード | 触る場面 | 失敗時の症状 |
|---|---|---|
| A / AAAA | ルートドメインやサブドメインをIPへ向ける | サイトに繋がらない、古いサーバーへ行く |
| CNAME | CDNやホスティング先へ別名で向ける | CDNの検証が終わらない、証明書発行が止まる |
| MX | メール配送先を決める | 外部メールへ届かない、同一ドメイン宛だけ失敗する |
| TXT | SPF、DKIM、DMARC、所有権確認 | メールが迷惑判定される、SaaSのドメイン確認が通らない |
| CAA | 証明書を発行できる認証局を制限する | Let’s EncryptやCDNの証明書発行が失敗する |
| HTTPS / SVCB | HTTP接続先やALPNなどの接続パラメータを配る | ブラウザだけ別の接続経路を選ぶ、HTTP/3やECHまわりの挙動がずれる |
| NS | そのゾーンの権威DNSを決める | 管理画面の値を変えても外から反映しない |
WordPressで同一ドメイン宛メールが届かない問題 では、MXがGoogle Workspaceを向いていても送信元サーバー側の配送設定で止まっていた。
MXをGoogle Workspaceへ向けても、Webサーバー側のローカル配送設定が残っていると、同一ドメイン宛だけ外へ出ずに失敗する。
DNSで配送先を確認したあと、アプリがどのSMTP経路を使っているかまで見る。
dig example.com MX
dig example.com TXT
dig _dmarc.example.com TXT
dig example.com CAA
dig example.com HTTPS
メール系は特に「DNSレコードは正しいのにアプリの送信経路が違う」ことがある。
DNSの切り分けは終点ではなく、次に見る場所を決めるための材料になる。
CAAは、ドメイン側が「どの認証局に証明書発行を許可するか」をDNSで示すレコードだ。
RFC 8659ではCAAを証明書発行前に認証局が確認するための仕組みとして定義している。
サイトは引けるのに証明書だけ発行できない場合、AやCNAMEだけでなくCAAも見る。
セキュリティ境界としてのDNSは粗いが速い
原典では、DNSを攻撃の前段で止めるチョークポイントとして扱っている。
端末が悪意あるドメインへ接続する前に、再帰リゾルバが名前解決を拒否すれば、HTTPリクエストやTLS接続まで進まない。
ただしDNSで分かるのは、基本的には「どの名前を引こうとしたか」だ。
同じ正規ドメインの中に悪意あるパスがある場合、DNSだけでは判別できない。
逆に、攻撃者が新しいドメインを使い捨てると、ブロックリストの反映前に通る。
DNSトンネリングも同じ見方になる。
端末が abcd1234.attacker.example のような長いサブドメインを大量に引き、問い合わせ名の中へデータを詰める手口だ。
通信先IPだけを見ていると普通のDNS問い合わせに見えるが、問い合わせ名の長さ、ランダムさ、NXDOMAINの比率、同じ親ドメインへの連続問い合わせを見ると異常として拾いやすい。
ブラウザ操作系のMCPでも、DNSは権限境界に絡む。
Playwright MCPの記事 で触れたDNSリバインディングは、外部ドメインの名前解決結果を後からローカルIPへ変え、ブラウザ経由で内部サービスへ触らせる攻撃だった。
このタイプでは、URLの文字列が外部ドメインでも、接続先IPがあとからローカルアドレスへ変わる。
名前解決後のIPレンジ、Hostヘッダー、Origin、ローカルアドレスへの到達可否を合わせて縛る。
DNSSECは改ざん検出であって到達性保証ではない
キャッシュポイズニング対策としてDNSSECが出てくる。
DNSSECは、権威DNSが返すレコードに署名を付け、再帰リゾルバがその署名を検証できるようにする仕組みだ。
途中で偽のIPを混ぜられても、署名検証で弾ける。
見るコマンドはこれで足りる。
dig example.com A +dnssec
dig example.com DS
dig example.com DNSKEY
DNSSECが守るのは、名前解決の応答が改ざんされていないこと。
サーバー自体の侵害、CDN設定ミス、期限切れ証明書、アプリの脆弱性は別の層に残る。
RFC 4033も、DNSSECの範囲をデータ起源認証とデータ完全性として説明している。
通信の秘匿、サーバーの安全性、名前解決サービス自体の可用性まではDNSSECの仕事ではない。
署名鍵のローテーションやDSレコードの更新を間違えると、検証するリゾルバからだけ名前解決が失敗する。
この場合は、検証するリゾルバ、検証を抑えた問い合わせ、親ゾーンのDS、子ゾーンのDNSKEYを分けて見る。
dig @1.1.1.1 example.com A +dnssec
dig @1.1.1.1 example.com A +dnssec +cd
dig example.com DS
dig example.com DNSKEY
VLESS + REALITYをXray-coreだけで立てた記事 では、REALITYが偽装先の証明書を返すかを openssl s_client で確認した。
あの確認はDNSではなくTLS層の話だ。
DNSで正しい名前が引けても、SNIと証明書、プロキシの終端位置がズレていれば、外から見える挙動は壊れる。
切り分けは権威DNSから戻る
DNSで詰まったら、ブラウザから順に掘るより、権威DNSから戻るほうが迷いにくい。
# 権威DNSの一覧
dig example.com NS
# 権威DNSを直接指定してAレコードを見る
dig @ns1.example-dns.com www.example.com A
# 再帰リゾルバ越しの答えとTTLを見る
dig @1.1.1.1 www.example.com A
# 手元のOSが見ている答えを見る
dig www.example.com A
権威DNSが違う答えを返すなら、ゾーン設定を見る。
権威DNSは正しく、再帰リゾルバが古いならTTL待ちかリゾルバ側のキャッシュ。
再帰リゾルバも正しく、ブラウザだけ失敗するなら、OSキャッシュ、ブラウザのDNSキャッシュ、HTTPSレコード、プロキシ、証明書、アプリ側の接続先を疑う。
「DNSは電話帳」という比喩は入口としては分かりやすいが、変更作業や障害対応で効くのは、電話帳の中身より「どの段がまだ古いページを持っているか」を権威DNS側から確認していく見方だった。
参考
- Understanding DNS - DEV Community
- RFC 1034 - Domain names - concepts and facilities
- RFC 2308 - Negative Caching of DNS Queries
- RFC 4033 - DNS Security Introduction and Requirements
- RFC 8659 - DNS Certification Authority Authorization Resource Record
- RFC 9460 - Service Binding and Parameter Specification via the DNS
- BIND 9 Manual Pages - dig