今更だけどDNSキャッシュポイズニングについてまとめてみた

## 最近流行りのようなので
最近またDNSキャッシュポイズニングが増えてきており、さらにOpenSSLのHeartbleed脆弱性(CVE-2014-0160)による秘密鍵流出の危険性などで、たとえSSLで通信していても安全とはいえない可能性が高くなってきました。

[複数の DNS 実装にキャッシュポイズニングの脆弱性](https://jvn.jp/vu/JVNVU800113/index.html)
[OpenSSL の heartbeat 拡張に情報漏えいの脆弱性](https://jvn.jp/vu/JVNVU94401838/index.html)

今まではSSLで通信していればDNSが汚染されていても異常を検出できましたが(瀬戸際ですが)、SSLの安全性が担保できなければ安全性の根拠はDNSのみになってしまいます。

## 対抗策

DNSキャッシュポイズニングへの対抗策として、以下の方法が知られています。

- ソースポート、内部IDのランダム化
従来のDNSのパケットのソースポートや内部IDには規則性があるため攻撃者に類推されてしまい、偽のDNS応答パケットを送りつけられることでDNSキャッシュが汚染されてしまいます。
そこで、ソースポートや内部IDをランダムにすることで、成功する確率を落とす方法がとられています。

- DNSSECの利用
DNSSECとはDNSにおける応答の正当性を検証するための拡張仕様で、デジタル署名によってDNSの応答が改ざんされていないことを確かめることができます。
しかし、DNSSECだけでは攻撃者がDNSキャッシュポイズニングの手法を利用して意図的に検証エラーを発生させることでDoS攻撃が成立してしまうため、前述のランダム化も行うことが必須事項となります。

## 対抗策の設定方法
**必ずDNSサーバは最新のものを使うようにしてください。**

### ランダム化の設定
BINDにおいては、基本的に最新版であれば問題はないはずです。
しかし、query-sourceやquery-source-v6の設定がされている場合、ソースポートが固定化されてしまうので設定を確認してください。
参考: [DNS キャッシュポイズニング攻撃に関する注意喚起](https://www.jpcert.or.jp/at/2014/at140016.html)

Unboundにおいては、デフォルトで1024番以上のIANAで割り当てられていないポートを使うので問題ないはずです。

### DNSSECの設定
BINDにおけるDNSSECの設定は、以下のページが参考になります。
[キャッシュDNSサーバのDNSSEC対応](http://www.atmarkit.co.jp/ait/articles/1106/01/news137.html)

また、Unboundでの設定方法は、日本Unboundユーザ会のページが参考になります。
[Unbound: DNSSECを有効にする方法](http://unbound.jp/unbound/howto_anchor/)

基本的に行わなければいけないことは、

- ルートゾーンのトラストアンカーを取ってくる(最近のディストリには含まれていることが多い)
- DNSSECを有効にする

です。

## 設定の確認方法

### ランダム化の確認
DNS-OARCのWeb-based DNS Randomness Testが使えます。
[Web-based DNS Randomness Test](http://entropy.dns-oarc.net/)

Test My DNSボタンをクリックして待つだけで結果が出てきます。
それぞれのネームサーバに対して、ソースポートと内部IDのランダム性を確認してくれます。
表示される情報は以下の通りです。

- サンプル数
- ユニークなポート数
- 範囲
- 標準偏差
- ランダムなビット数(最高16)
- 値の一覧

### DNSSECの確認
digコマンドを使って、ルートに対してDNSSECレコードを問い合わせます。

```console
$ dig +dnssec .
```

結果のflagsの欄に、adフラグがあれば署名の検証に成功しています。

次に、署名の検証に必ず失敗するテスト用のドメインに対して問い合わせを行います。

```console
$ dig dnssec-failed.org
```

結果のstatusの欄が、SERVFAILになっていれば正常です。

## IPマスカレード内におけるDNSの取り扱い

最近では、セキュリティの確保やIPv4アドレスの消費を抑えるために、IPマスカレードを使うことが増えています。
しかしここで問題になるのが、ソースポートのランダム化がIPマスカレードの処理によって妨害されないか、という問題です。
例えば、以下のようなネットワークを想定します。

```
クライアント
　↓
内部DNSサーバ
　↓
ルータ
　↓
インターネット 外部DNSサーバ
```

クライアントが内部DNSサーバに対してwww.yahoo.co.jpを問い合わせるとすると、内部DNSサーバはwww.yahoo.co.jpのレコードを知らないので外部DNSサーバへ問い合わせを投げます(再帰問い合わせ)
このとき、内部DNSサーバに適切な設定がされていてランダム化が行われていたとしても、ルータを通る際にポートが変換されるため、使用可能なポートを順番に割り当てるような通常のルータの場合にはソースポートのランダム化が無効になってしまいます。

参考: [DNSキャッシュ・ポイズニングとNAT機能](http://itpro.nikkeibp.co.jp/article/COLUMN/20080826/313472/)
>市場に出回っているNAT機器でUDPソース・ポートをランダムに選択するものは存在しない。したがって、アップデート適用済みDNSクライアントとDNSサーバーがNAT機器の配下にある場合、DNSキャッシュ・ポイズニング攻撃に対してぜい弱なままである。最新のアップデートで導入されたソース・ポートのランダム性は、NAT機器で失われてしまうのだ。

IPマスカレード環境下において、以下のような使用方法はDNSキャッシュポイズニングに対して弱くなります。

- クライアントがインターネット上のDNS(Google Public DNSなど)をDNSサーバに設定する
- 内部DNSのフォワード先としてインターネット上のDNSサーバを設定する

### どうすれば？

以下のような方法で緩和できる可能性があります。

- DNSのフォワードを行うのをルータのみに限定する
ルータのDNSサーバの実装による(ソースポートのランダム化などができる必要がある)
また、クライアントはDNSサーバをルータに向けることになる
可能であればクライアントから外部のDNSへ直接アクセスできないようにすることを推奨

- プロバイダ内で経路が完結するようなDNSサーバを使う
DNSのパケットをインターネット上に出さないことで、改ざん可能な経路の範囲を狭める

- DNSサーバをDMZ上に逃がして、グローバルIPアドレスを振る
- IPv6を経由してインターネット上のDNSサーバを使う
IPv6ならすべてがグローバルIPアドレスになるのでIPマスカレードの問題はなくなる
試してないので可能かどうかは微妙なところです(AAAAレコードを優先して返してくる可能性とかがあるかも)

## 最後に
DNSについてよく知っている人なら今更な内容だと思います。
また、中の人がDNS noobな可能性も否定できないので、間違ったことを書いている可能性もあります。そのときはお知らせください。