メインコンテンツへスキップ
友田 陽大
実践ネットワーク攻撃と防御
セキュリティ
ネットワーク
TCP/IP
脆弱性診断
ホワイトハッカー
倫理的ハッキング

DNSスプーフィング・キャッシュポイズニングの仕組みと防御【2026】— RFC 5452 と DNSSEC で名前解決を守る

名前解決を乗っ取る DNS スプーフィング/キャッシュポイズニングを、Kaminsky 攻撃の原理から RFC 5452・DNSSEC(RFC 4033-4035)の防御まで体系的に解説。なぜ UDP の DNS は偽答を信じてしまうのか、送信元ポートランダム化・トランザクションIDがどう推測困難性を作るのか、そして DNSSEC の署名検証・DoH/DoT による暗号化までを、型安全なコードと設定例で示します。すべて自分のリゾルバに閉じた合法手順です。

公開日
読了時間
9分
著者
友田 陽大
シェア

あなたがブラウザに bank.example と打ち込んだその瞬間、最初に起きるのは名前解決——ドメイン名をIPアドレスに変換する DNS の問い合わせです。もしこの一手を攻撃者に乗っ取られたら? URL バーには正しいドメインが表示されたまま、接続先だけが攻撃者のサーバーにすり替わります。これが DNSスプーフィングであり、リゾルバのキャッシュごと毒するキャッシュポイズニングは、一度の成功で多数の利用者を巻き込む、極めて影響範囲の広い攻撃です。

この記事は、ネットワークペンテストの全体像に続き、DNS 攻撃の原理を歴史的な Kaminsky 攻撃から解き、RFC 5452 と DNSSEC による防御を設定とコードで示します。根本にあるのは、初期DNSが UDP上の「認証なき応答」を信じてしまうという、TCP/IP の信頼の前提です。

安全地帯の徹底:本記事の検証は、すべて隔離ラボの**自分が立てたDNSリゾルバ(例 BIND/Unbound のローカル実験機)**に対してのみ行います。公開リゾルバや他者のDNSへ偽答を注入する行為は、不正アクセス禁止法違反であり、社会インフラへの攻撃です。検証してよいのは自分の資産だけ——法律の記事を必ず先に。


1. DNS が騙される仕組み — 「速く・それらしい偽答」

キャッシュDNSリゾルバが bank.example の問い合わせを受け、まだキャッシュにない場合、上位の権威サーバーへ問い合わせます。攻撃者の狙いは、正規の権威サーバーの応答より先に、偽の応答をリゾルバに信じさせることです。

リゾルバが偽答を受け入れてしまうには、偽答が次をすべて満たす必要があります。

正規応答とマッチさせるべき要素:
  1. トランザクションID(16ビット)が問い合わせと一致
  2. 送信元/宛先ポートが一致
  3. 質問セクション(QNAME/QTYPE)が一致
  4. 正規応答より「速く」届く(レース)

かつてリゾルバは送信元ポートを固定し、トランザクションIDも予測しやすい実装がありました。すると攻撃者が当てるべきは実質**16ビットのID(65,536通り)**だけ。さらにキャッシュ済みなら次のTTLまで待つ必要がありますが——

1.1 Kaminsky 攻撃(2008)— DNS を作り直させた一撃

2008年、Dan Kaminsky は決定的な手法を公表しました。存在しないサブドメイン(random123.bank.example)を大量に問い合わせ、それぞれに対して「bank.example の権威サーバーは攻撃者のIPだ」という委任(NS/glue)を含む偽答を撃ち込む。存在しない名前なのでキャッシュのTTL待ちを回避でき、何度でも高速に試行できる。一度当たれば、ドメイン全体の権威がキャッシュに毒として刻まれます。

これは「16ビットIDの推測は現実的に破れる」ことを示し、DNS の防御を根本から見直させました。その答えが次の RFC 5452 と DNSSEC です。


2. 防御①:RFC 5452 — 推測を「非現実的」にする

RFC 5452(Measures for Making DNS More Resilient against Forged Answers, 2009)は、偽答を受け入れにくくする実装ルールを標準化しました。中心はエントロピーの追加です。

  • 送信元ポートのランダム化:問い合わせごとに送信元UDPポートをランダムに選ぶ。攻撃者はIDに加えてポート(最大16ビット)も当てねばならず、組み合わせのエントロピーが実質約32ビットに跳ね上がる。総当たりは非現実的になる。
  • トランザクションIDのランダム化:予測可能なIDを避け、暗号的に乱数化する。
  • 0x20 エンコーディング(補助):QNAME の英字の大小をランダム化し、応答でそれが保たれるかを追加の検証材料にする。
# 自分のリゾルバが送信元ポートをランダム化しているかを確認(自分の資産で)
# 良い例:問い合わせごとに source port がばらける(高エントロピー)
dig +short porttest.dns-oarc.net TXT @127.0.0.1
#   → "GREAT" 等(リゾルバのポートランダム性の評価が返る)

重要な限界:RFC 5452 は「推測を困難にする」確率的防御であって、「不可能にする」ものではありません。経路上にいる(MITM 済みの)攻撃者にはエントロピーが意味をなさない。だから次の DNSSEC が要ります。


3. 防御②:DNSSEC — 「言った者勝ち」を暗号で終わらせる

DNSSEC(RFC 4033/4034/4035)は、DNS 攻撃への本質的な答えです。発想は明快——権威サーバーが応答にデジタル署名し、リゾルバがその署名を検証する。偽答は正しい署名を作れないため、検証で弾かれます。

DNSSEC の信頼の連鎖(chain of trust):
  ルート(.) の鍵 ──署名──► .example の鍵 ──署名──► bank.example のレコード
       ▲                                                    │
       └──── リゾルバはルートの鍵(トラストアンカー)から ◄────┘
              署名を辿って検証。1つでも署名が合わなければ SERVFAIL(拒否)
  • RRSIG:各レコードセットへの署名。
  • DNSKEY:検証に使う公開鍵。
  • DS:親ゾーンに置かれ、子ゾーンの鍵を保証する(連鎖の輪)。

検証が成功すると、応答に AD(Authenticated Data)ビットが立ちます。偽答は署名検証に失敗し、リゾルバは結果を返しません。

# DNSSEC 検証が効いているかを確認(AD フラグが立つか)
dig +dnssec bank.example @127.0.0.1
#   ヘッダの flags に "ad" があれば、署名検証済みの真正な応答
#   RRSIG レコードが応答に含まれる

3.1 アプリ/リゾルバ側で検証を「要求」する

権威ゾーンに署名してあっても、リゾルバが検証しなければ意味がありません。検証するリゾルバを使うことが利用者側の責務です。

import { Resolver } from "node:dns/promises";

/**
 * DNSSEC 検証を行うリゾルバ(例:ローカルの validating resolver)を明示的に指定する。
 * OS のデフォルトに任せず「検証するリゾルバ」へ向けることで、毒入りキャッシュを避ける。
 * 失敗(SERVFAIL)は握りつぶさず例外として扱い、安全側(接続しない)に倒す。
 */
export async function resolveValidated(hostname: string): Promise<readonly string[]> {
  const resolver = new Resolver();
  resolver.setServers(["127.0.0.1"]); // 検証する自前/信頼リゾルバ(Unbound 等)
  try {
    return await resolver.resolve4(hostname);
  } catch (err) {
    // SERVFAIL は「署名検証に失敗した可能性」を含む。曖昧なまま接続しない。
    throw new Error(`DNS resolution failed (possible DNSSEC failure) for ${hostname}: ${(err as Error).message}`);
  }
}

4. 防御③:DoH / DoT — 経路を暗号化する

DNSSEC は応答の**真正性(改ざんされていないか)**を守りますが、機密性(誰が何を引いたかを盗み見られない)は守りません。また、利用者からリゾルバまでの経路はなお平文です。ここを埋めるのが通信路の暗号化です。

  • DoT(DNS over TLS, RFC 7858:DNS を TLS(853番)で運ぶ。
  • DoH(DNS over HTTPS, RFC 8484:DNS を HTTPS(443)で運ぶ。検閲・盗聴に強い。
役割分担(両方が要る):
  DNSSEC  … 応答の真正性(偽答を弾く)        ← 権威〜リゾルバの「中身」を守る
  DoH/DoT … 経路の機密性・完全性(盗聴/改ざん) ← 利用者〜リゾルバの「経路」を守る

DNSSEC と DoH/DoT は競合ではなく補完です。前者は「答えが本物か」、後者は「経路が覗かれないか」。本番では両方を組み合わせます。


5. 設計者のためのチェックリスト

自社ドメインと名前解決を守るために、設計・運用で確認すべき点です。

  • 自社の権威ゾーンを DNSSEC 署名しているか(ドメインレジストラ/DNSサービスで DS を親に登録)。
  • アプリが向くリゾルバが DNSSEC 検証を行うか(マネージドリゾルバなら検証ポリシーを確認)。
  • リゾルバが送信元ポートをランダム化しているか(RFC 5452 準拠)。
  • クライアント〜リゾルバ間で DoH/DoT を使えているか。
  • DNS 解決失敗(SERVFAIL)を握りつぶさず、安全側(接続中止)に倒しているか。
  • そもそも最終防壁は TLS の証明書検証——名前解決が毒されても、正規証明書を持たない攻撃者サーバーには TLS が繋がらない(MITM 記事参照)。

多層で考える:DNS が毒されても TLS で守られ、TLS を狙う MITM は mTLS とゼロトラストで守られる。単一の防御に依存しないのがネットワークセキュリティの一貫した姿勢です。


6. まとめ

  • DNSスプーフィング/キャッシュポイズニングは名前解決を乗っ取る。Kaminsky 攻撃が16ビットID推測の危険を現実化し、DNS の防御を作り直させた。
  • RFC 5452:送信元ポート+IDのランダム化でエントロピーを約32ビット級へ。推測を非現実的にする確率的防御(経路上の攻撃者には無力)。
  • DNSSEC(RFC 4033-4035):署名と信頼の連鎖で偽答を弾く本質的防御。AD ビットが真正性の証。
  • DoH/DoT(RFC 7858/8484):経路の暗号化。DNSSEC(真正性)と補完関係で両方要る。
  • 最後はやはり TLS:名前解決が毒されても、証明書検証が攻撃者サーバーを拒む。多層防御。

次は、確立済みのTCP接続そのものを奪う**TCPセッションハイジャック / RSTインジェクション / IPスプーフィング**を、RFC 5961 の防御まで扱います。


私(友田 陽大)は、本番システムの DNS 設計(DNSSEC・プライベートDNS・解決失敗時のフェイルセーフ)と、TLS/証明書運用を含む“名前解決を信頼しきらない”設計を実装してきました。「自社ドメインを DNSSEC 署名したい」「DNS 解決の失敗が障害につながっている」「DoH/DoT を導入したい」——こうした名前解決の堅牢化を、攻撃者の視点で診断し、真正性と機密性の両輪で実装します。お気軽にご相談ください。

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・ 実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

この攻撃、あなたのネットワークで成立しませんか?

ネットワーク/インフラのペネトレーションテスト・堅牢化を承ります

この記事で扱った ARPスプーフィング・DNS汚染・セッションハイジャック・SYNフラッド・盗聴といった L2〜L4 の攻撃を、御社の構成で『どこが破れるか』を攻撃者の視点で診断し、RFC準拠の防御(DAI・DNSSEC・RFC 5961・BCP 38・TLS/mTLS・WAF/DDoS防御)まで設計・実装します。AWS マルチアカウントで多層ネットワーク(VPC・最小権限IAM・GuardDuty・WAF)を構築してきた知見で、攻撃面の最小化とゼロトラスト化を伴走します。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。

あわせて読みたい