DNSの再帰と反復、TCPフォールバックとEDNS0について。

DNSを使った名前解決の際にキャッシュDNSサーバーへの問い合わせと権威DNSサーバーへの問い合わせがありますが、それぞれ「再帰的問い合わせ」、「反復的問い合わせ」と言われています。どちらが再帰でどちらが反復だっけ?紛らわしいので整理しました。

そもそも再帰と反復の意味は国語辞典で調べると以下です。

「再帰」の意味:もう一度帰ってくること
「反復」の意味:同じことを何度も繰り返すこと

DNSサーバーの再帰的問い合わせと反復的問い合わせの仕組みを絵で書くと以下のようになります。

確かに権威DNSサーバーには何度も「反復」して問い合わせてます。キャッシュDNSサーバーには名前解決の要求をして応答が返って来ますが、これを「再帰」と呼んでいるのですね(再帰って当たり前のことのように思いますが)。

DNSの仕組みでもう一つ紛らわしいものがあって、TCPの53番ポートを使う場合とUDPの53番ポートを使う場合があります。TCPでもUDPでもどちらも53のポートです。なんの用途に53番ポートを使っているかというと名前解決とゾーン転送です。

TCPの53番ポート・・・ゾーン転送の際に使われる。
UDPの53番ポート・・・名前解決の際に使われる。

先ほど出てきた再帰的問い合わせと反復的問い合わせは名前解決なのでスタブリゾルバ、キャッシュDNSサーバー、権威DNSサーバーはみんなUDPの53番ポートを使ってやりとりしています。

ゾーン転送について説明します。通常の権威DNSサーバーはプライマリとセカンダリの2つのサーバーを用意します。冗長化ですね。このプライマリとセカンダリとでデータを同期する必要があり、その同期する作業をゾーン転送と呼んでいます。DNSのデータはゾーン情報と呼ばれているのでゾーン転送と言われるのでしょう。ゾーン転送の際にプライマリとセカンダリの権威DNSサーバーはTCPの53番ポートを使ってやりとりしています。

基本的には上に書いたとおりです。ただ、、、名前解決の際はTCPの53番ポートが使われる場合があります。UDPって言ったじゃないか!と言われるかもしれませんが。。ここらへんが少々ややこしいのですが、DNSができたときからDNSの名前解決のメッセージは512バイトまでと決められていてDNSの応答メッセージのサイズが512バイトを超える場合は送信元にメッセージを512バイトに切り詰めたことを示す情報(TCビット)を送って、再度、送信元がTCPを使って問い合わせをし直します。これを「TCPフォールバック」と呼んでいます。

TCPフォールバックが起きることはDNSが出来た当時はほとんどありませんでした。その後、DNSの仕組みを悪用した犯罪(DNSキャッシュポイズニング)がでてきて、対抗策としてDNSSEC(Domain Name System Security Extensions)という仕組みが考え出されました。DNSキャッシュポイズニングとはキャッシュDNSサーバーに偽の情報を送りこみ、問い合わせてきたユーザーを偽のページに誘導することです。またDNSSECとは公開鍵暗号と電子署名の仕組みを利用してキャッシュDNSサーバーと権威DNSサーバーとの通信を安全にやりとりする技術です。

DNSSECによりDNSキャッシュポイズニングを防ぐことができるようになりましたが、DNSSECを使うことにより認証等の情報が追加されてDNSのメッセージサイズが512バイトを超えてしまうことが発生するようになりました。つまり、TCPフォールバックが発生します。名前解決にTCPを使うことはオーバーヘッドが高く応答に時間がかかるため、UDPの新たな仕組みとしてEDNS0(Extension Mechanisms for DNS version 0)が考え出されました。EDNS0ではメッセージサイズを512バイトから拡張しています(EDNS0の特徴はまだ他にもあるが割愛)。

なお、DNSSECおよびEDNS0が使われるのはキャッシュDNSサーバーと権威DNSサーバー間です。PCのスタブリゾルバとキャッシュDNSサーバー間はDNSSECを使わないのでDNSのメッセージが512バイトを超えることはあまりないと思いますが、超えた場合はTCPフォールバックが発生します。

ただ今後はIPv6が普及してきてDNSのメッセージサイズは大きくなるため、PCのスタブリゾルバとキャッシュDNSサーバー間もEDNS0が使われるようになるんじゃないかと思います。

[2019/09/28 追記]
DNSキャッシュポイズニングの対抗策としてDNSSECを書きましたが、別の措置として「名前解決する際のポートをランダムに変更する」という手もあります。名前解決はポート53を使うという説明と食い違ってしまうので補足しておきます。

そもそもDNSはUDPを使用しているため通信の信頼性はDNSのプロトコルで行っています(TCPの場合は再送制御や順序制御をTCPが行ってくれているがUDPはそういうものがないためDNSの実装の中でデータが正しく届いているかを確認する必要がある)。

キャッシュDNSサーバーと権威DNSサーバー間の通信を考えてみます。キャッシュDNSサーバーのリクエスト(名前解決の要求)に対するレスポンス(権威DNSサーバーからの応答)の正当性を確認する際、レスポンスの中に含まれる以下の要素が合致すればその応答は正しいとキャッシュDNSサーバーは判断します。

  • リクエスト時に設定した「クエリーID」
  • キャッシュDNSサーバーの「IPアドレス」
  • キャッシュDNSサーバーの「ポート番号」

このうちIPアドレスは既知で、ポート番号もDNSのサーバーソフトから推測できます(もしくは、53番ポートのまま使っているサーバーもあります)。そうするとクエリーIDを一致させることができれば偽の応答をキャッシュDNSサーバーに送り込むことができます。このクエリーIDは16ビットの数値のため全部で65536通りしかありません。今のパソコンの処理能力であれば総当りで攻撃パケットを送り込むことが可能で、この攻撃パケットが権威DNSサーバーの応答より早くキャッシュDNSサーバーに届いてクエリーIDが一致すれば攻撃成功となります。

なので、この対策としてキャッシュDNSサーバーの送信する際のポートをランダムにするというのがDNSキャッシュポイズニングの対策となります。
※先に書いた「名前解決する際のポートをランダムに変更する」というのは以下の絵のことです。

ただこの方法は万全ではなく、ポート番号が増えた分の総当りさせるパターンを増やすことで攻撃成功の可能性を低くさせているだけです(とは言え、クエリーIDだけでは不十分なのでやっておいたほうが良い)。DNSSECのほうが安全です。

[2024/03/25 追記]
PCのスタブリゾルバとキャッシュDNSサーバーとの通信を保護する手段としてDoH(DNS over HTTPS)があり、これについても書いておきます。

通常の名前解決はUDPを使いますが、DoHではTCPの443番ポートを使います。TCPではPCとキャッシュDNSサーバーとの間でコネクションを確立するので攻撃者がパケットを偽装しにくくなります。またHTTPSを使用することで通信が暗号化されるので安全性が高まります。

DoHを使うにはキャッシュDNSサーバーがDoHに対応している必要があります。多くのパブリックDNS(Cloudflare社とAPNICが運営する1.1.1.1、Google社の運営する8.8.8.8など)は、DoHに対応しています。

PC側でも主要なWEBブラウザはDoHに対応しています。ただDoHを使うには設定画面でDoHを指定する必要があります。以下はFirefoxの例です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です