DNS キャッシュポイズニング(DNS Cache Poisoning)
概要
DNS キャッシュポイズニング(DNS Cache Poisoning) は、再帰リゾルバーのキャッシュに偽の DNS 応答を注入し、正規のドメイン名に対して攻撃者が管理する IP アドレスを返させる攻撃です。RFC 5452(2009年)で対策手法が体系化されています。
DNS は 1987 年の RFC 1035 で設計された当初、応答の正当性を検証する仕組みを持ちませんでした。再帰リゾルバーが権威ネームサーバーに問い合わせる際、応答が本物かどうかを判断する手段がトランザクション ID(16 ビット、65,536 通り)と送信元ポートの照合だけでした。攻撃者がこれらを推測して偽の応答を先に送り込むことができれば、リゾルバーは偽のレコードをキャッシュに保存します。
キャッシュに偽レコードが注入されると、そのリゾルバーを利用する全ユーザーが、正しいドメイン名を入力しても攻撃者のサーバーに誘導されます。フィッシングサイトへの誘導、マルウェア配布、認証情報の窃取に悪用されます。TTL が設定されている限り偽レコードはキャッシュに残り続けるため、攻撃者が一度成功すれば長時間にわたり影響が持続します。
仕組み
基本的な攻撃フロー、2008 年の Kaminsky 攻撃がもたらした脅威の変化、および偽の応答が注入される技術的な理由を解説します。
基本的な攻撃フロー
DNS キャッシュポイズニングの基本的な流れは次のとおりです。
- 攻撃者が標的の再帰リゾルバーに対して
example.comの DNS クエリを発生させる - リゾルバーが権威ネームサーバーに問い合わせを開始する
- 攻撃者が権威サーバーよりも先に、偽の応答パケットをリゾルバーに送信する
- リゾルバーが偽の応答を正規の回答として受け入れ、キャッシュに保存する
- 以降、TTL が切れるまで
example.comへの問い合わせに偽の IP アドレスが返る
偽の応答が受け入れられるには、トランザクション ID、送信元ポート、応答元 IP アドレスが正規の問い合わせと一致する必要があります。従来の実装ではポートが固定されていたため、攻撃者が推測すべきパラメーターはトランザクション ID の 65,536 通り のみでした。
Kaminsky 攻撃(2008年)
2008 年にセキュリティ研究者 Dan Kaminsky が発表した手法は、DNS キャッシュポイズニングの脅威を根本的に変えました。
従来の攻撃は、対象ドメインのレコードが既にキャッシュにある場合、TTL が切れるまで攻撃の機会がありませんでした。Kaminsky 攻撃はこの制約を回避します。
攻撃者 → リゾルバー: random123.example.com の A レコードを問い合わせ
↓
リゾルバー → 権威サーバー: random123.example.com を問い合わせ
↓
攻撃者 → リゾルバー: 偽の応答(Authority セクションに example.com の NS を偽装)
攻撃者は存在しないサブドメイン(random123.example.com)を問い合わせることで、キャッシュにヒットしない新しいクエリを無制限に発生させます。偽の応答の Authority セクションに example.com のネームサーバー情報を含め、example.com のゾーン全体を乗っ取ります。ランダムなサブドメインを次々と変えて試行できるため、秒間数百回の試行が可能です。
この発表により、DNSSEC の普及が加速し、送信元ポートのランダム化が緊急パッチとして全主要 DNS ソフトウェアに適用されました。
なぜ偽の応答を注入できるのか
DNS クエリは UDP で通信します。TCP と異なりコネクション確立のハンドシェイクがないため、送信元 IP アドレスを偽装(スプーフィング)して応答パケットを送信できます。
リゾルバーが正規の応答と偽の応答を区別するためのパラメーターは次の 3 つです。
| パラメーター | ビット数 | 推測難易度 |
|---|---|---|
| トランザクション ID | 16 ビット | 65,536 通り |
| 送信元ポート(ランダム化前) | 固定 | 推測不要 |
| 送信元ポート(ランダム化後) | 約 16 ビット | 約 65,536 通り |
ポートが固定されていた時代は、16 ビットのトランザクション ID だけで攻撃が成立しました。ポートランダム化後はエントロピーが合計 約 32 ビット に増え、攻撃の成功確率は大幅に低下しています。
対策
キャッシュポイズニングへの防御は、ソースポートランダム化による推測難易度の引き上げと、DNSSEC や暗号化 DNS による根本的な応答検証に大別されます。
ソースポートランダム化
RFC 5452(2009年)で規定された対策です。DNS クエリの送信元ポートを毎回ランダムに変更することで、攻撃者が推測すべきパラメーターのエントロピーを増やします。BIND、Unbound、dnsmasq など主要な DNS ソフトウェアは 2008 年の Kaminsky 攻撃公表直後に緊急パッチを適用し、現在のバージョンではすべて標準で有効です。
DNSSEC
DNSSEC(RFC 4033〜4035、2005年)は DNS 応答にデジタル署名を付与し、リゾルバーが署名を検証して改ざんされた応答を拒否する仕組みです。キャッシュポイズニングに対する根本的な対策ですが、ドメイン側が DNSSEC を有効にしていなければ効果がありません。
2026 年初頭の時点で、ドメイン全体の DNSSEC 署名率は約 4.3% にとどまっています。Google Public DNS(8.8.8.8)と Cloudflare DNS(1.1.1.1)は DNSSEC 検証を標準で有効にしているため、署名済みドメインに対する偽応答は検出されます。
DNS over HTTPS / DNS over TLS
DNS over HTTPS(DoH、RFC 8484、2018年)と DNS over TLS(DoT、RFC 7858、2016年)は、リゾルバーとの通信を暗号化します。暗号化によりパケットの偽装が困難になるため、経路上でのキャッシュポイズニングを防ぎます。ただし、リゾルバー自身が権威サーバーに問い合わせる経路がプレーン UDP の場合、その区間でのポイズニングリスクは残ります。
0x20 エンコーディング
DNS のドメイン名照合はケースインセンシティブ(大文字・小文字を区別しない)ですが、送信時の大文字小文字をランダムに変えて(例: eXaMpLe.CoM)応答に同じパターンが含まれるかを検証する手法です。Google Public DNS がこの技術を実装しています。正式な RFC にはなっていませんが、追加のエントロピーとして機能します。
確認方法
自分の再帰リゾルバーがソースポートランダム化を実施しているかどうかは、複数の DNS クエリの送信元ポートを観察することで確認できます。
# 送信元ポートの変化を確認する(tcpdump で UDP 53 番ポートのトラフィックを観察)
sudo tcpdump -n -c 10 udp port 53
送信元ポートが毎回異なっていれば、ランダム化が有効です。同じポートが繰り返し使われている場合、ポイズニングに対して脆弱な状態です。
DNSSEC の検証が有効かどうかは、署名済みドメインへのクエリで ad(Authenticated Data)フラグを確認します。
# DNSSEC 検証の確認
dig A example.com +dnssec @8.8.8.8
応答ヘッダーの flags: に ad が含まれていれば、リゾルバーは DNSSEC 検証に成功しています。
特定のドメインが DNSSEC に対応しているかを確認するには、DS レコードと DNSKEY レコードの有無を調べます。
# DS レコードの確認(親ゾーンに登録されている)
dig DS example.com
# DNSKEY レコードの確認(ゾーン自体の公開鍵)
dig DNSKEY example.com
外部の視点からも確認したい場合は、Labee Dev Toolbox の DNS API を使うと、外部の視点から見た結果を取得できます。
curl "https://labee.dev/api/dns?domain=example.com&type=A"
レスポンスは次の形式で返ります。
{
"success": true,
"data": {
"domain": "example.com",
"records": {
"A": [
{ "address": "93.184.216.34", "ttl": 86400 }
]
}
},
"error": null,
"meta": { "responseTime": 55 }
}
data.records.A フィールドに A レコードの address と ttl が返ります。自分のリゾルバーの dig 結果と Labee API の結果を比較して、異なる IP アドレスが返っている場合は、どちらかのリゾルバーのキャッシュが汚染されている可能性があります。
# 自分のリゾルバーと比較
dig A example.com
上記の結果と Labee API の結果が一致するかを確認します。
よくある問題
キャッシュポイズニングが疑われる場面では、正常なキャッシュ動作との区別や、複数リゾルバー間の結果の差異が混乱の原因になります。
意図しないリダイレクト
キャッシュポイズニングが成功すると、ユーザーは正しい URL を入力しているにもかかわらず、偽のサーバーに誘導されます。HTTPS を使用しているサイトであれば、偽サーバーは正規の TLS 証明書を持たないため、ブラウザーが証明書エラーを表示します。ただし、攻撃者が DNS 経由で CAA レコードも改ざんし、正規の CA から証明書を取得するケースも理論上は存在します。
対策として、HSTS(HTTP Strict Transport Security)を有効にしておくと、ブラウザーが HTTPS 接続を強制するため、証明書のない偽サーバーへの平文接続を防げます。
パブリック DNS と ISP DNS の結果が異なる
dig @8.8.8.8 と dig @ISPのDNS で異なる IP アドレスが返る場合、いくつかの原因が考えられます。キャッシュの更新タイミングの差異(正常な挙動)、ISP の DNS がポイズニングされている、または ISP が意図的に DNS 応答を書き換えている場合です。
複数のパブリック DNS(8.8.8.8、1.1.1.1、9.9.9.9)で結果が一致し、ISP の DNS だけ異なる場合は、ISP 側の問題である可能性が高いと判断できます。
レコード変更後に古い IP が返り続ける(ポイズニングとの区別)
DNS レコードを変更した後に古い IP が返り続ける現象は、通常のキャッシュの TTL 切れ待ちであり、ポイズニングではありません。権威サーバーに直接問い合わせて新しいレコードが返るかどうかで区別できます。
# 権威サーバーに直接問い合わせ(キャッシュをバイパス)
dig A example.com @ns1.example.com +norec
# 再帰リゾルバー経由(キャッシュが返る)
dig A example.com @8.8.8.8
権威サーバーが新しい IP を返し、再帰リゾルバーが古い IP を返している場合は、正常なキャッシュ動作です。権威サーバーの結果自体が想定外の場合は、権威サーバー側の設定を確認してください。
オープンリゾルバーの悪用
自組織で運用する再帰リゾルバーがインターネット全体からのクエリを受け付ける状態(オープンリゾルバー)になっていると、キャッシュポイズニングの標的になりやすくなります。外部からのクエリを受け付けることで攻撃者がクエリを発生させ、偽応答の注入を試みる機会を与えます。再帰リゾルバーへのアクセスは自組織のネットワークからのみ許可してください。