【DNS暗号化】セキュリティの尻隠して頭隠さず...？「プライベートDNS」を利用しよう

本日のお題。
**セキュリティの穴、DNSを何とかしましょう！**

自宅用DNSサーバーを構築してからDNSがマイブームな今日この頃、[プライベートDNSについての記事](https://qiita.com/Usamimi_Latias/items/8e3df25e461963a88716)に出会いました。
以前から有効にはしていましたが、どんな動作をしているのか気になって調査してみました。

**追記**
先日`Microsoft`さんが華麗な手のひらクルーを発揮して`Windows 11`を公開しました。
さて、`Windows 11`では標準で`DoH`という暗号化DNSを利用可能です。
記事の最後に設定方法などを記しました。

# プライベートDNSとは？

`Android 9 (Pie)`以降のスマートフォンに実装されている機能で、以下のような感じでアクセスできると思います。

- `設定`→`その他の接続`→`プライベートDNS`
- `設定`→`ネットワークとインターネット`→`プライベートDNS`
- `設定`→`ネットワークとインターネット`→`詳細設定`→`プライベートDNS`

取り敢えずは`プライベートDNSを設定`を選択して`dns.google`や`one.one.one.one`などと打ち込むと設定が完了します。
ほぼ同様の内容ですが、[Google Public DNS](https://developers.google.com/speed/public-dns/docs/using#android)に詳しい説明が記載されています。
なお、`自動`に設定した場合は、設定中のDNSサーバーが本機能に対応している場合に有効となります。

## どういう機能？

[Android Developers Blog](https://android-developers.googleblog.com/2018/04/dns-over-tls-support-in-android-p.html)によりますと...

>Like HTTPS, DNS over TLS uses the TLS protocol to establish a secure channel to the server. Once the secure channel is established, DNS queries and responses can't be read or modified by anyone else who might be monitoring the connection. (The secure channel only applies to DNS, so it can't protect users from other kinds of security and privacy violations.)

要するに、**DNSの問い合わせを安全にしよう**ってことらしいです。

<details><summary>「DNSってなーに？」</summary>
DNSとは、電話でいうところの104番です。<br>
104をダイヤルして「東京特許許可局の電話番号を教えてください。」と言えば「xxxx-xx-xxxxです。」と教えてくれるでしょう。
<br>同様にDNSでは<b>ドメイン名のIPアドレスを教えてくれます</b>。
ドメイン名とは「qiita.com」のような<b>人間が理解しやすい住所</b>のこと。
対してIPアドレスは「123.456.789.xxx」というような<b>機械が理解しやすい住所</b>です。
<br>例えば、「example.com」というWebサイトにアクセスしようとした場合、サーバーに到達するために必要なのは「123.456.789.xxx」というIPアドレスになりますから「example.com => 123.456.789.xxx」という変換をしなくてはなりません。
この変換を担うのがDNSというわけです。
<br>今日、IPアドレスを直打ちするという機会はほぼありませんので、DNSは全ての通信の起点といっても過言ではない重要なシステムです。
</details>

DNSが策定された1980年当時、インターネットは今より小規模で単純だったのでDNSに機密性等は求められていなかったのですが、近年の著しい発達に伴い安心安全なプロトコルが必要になったため偉い人が[RFC 7858](https://tools.ietf.org/html/rfc7858)で策定したのが`DNS over TLS`（以下`DoT`）で、その`DoT`を適用してDNSの問い合わせを行うのが`プライベートDNS`の機能です。

<details><summary>DNSの危険性</summary>
DNSを保護することはプライバシー・セキュリティの両面から重要です。
DNSを監視していれば、その人がどんなサイトにアクセスしているのかが分かりますし、偽のIPアドレスを教えれば偽のサイトに誘導することも可能です
DNSパケットの監視や偽の応答は比較的簡単に実行できますから、公衆無線LANなどで安全性が侵害されるハードルはとても低いです。
<br><b>絶対にしませんし、してはいけませんが</b>、私が（誰でも）Macbookを持ってスタバに行けば、スタバのフリーWi-Fiに接続している人達の接続先ドメイン名を「専門的知識がなくても」監視することができ、それはすなわち、誰でも容易に監視されうることを意味します。
害は無いですが、「漫画村」や児童ポルノに対するフィルタリングもDNSの問い合わせに対して偽のIPアドレスを返すことで行われています。
このように、十分に悪用される可能性があるのできちんと対策する必要があります。
</details>

## DNS over TLS

`DoT`の仕組みは`HTTPS`と似たような感じとなっており、DNSサーバーとクライアント間で確立したセキュアなチャンネルを用いてDNSの通信を行い、第三者が閲覧・改竄できないようにしています。

読んで字のごとく、`TLS`というセキュアなトンネルを通る`DNS`通信というわけです。
実際にどのような通信をしているかは、以下の検証を見てください。

詳しく知りたい方はJPNICさんで公開されている資料[「DoH/DoT入門」](https://www.nic.ad.jp/ja/materials/iw/2019/proceedings/d3/d3-yamaguchi.pdf)が分かり易いのでおすすめです。

# 実際の通信を見てみよう

DNSがどのようにやり取りされているのか[Wireshark](https://www.wireshark.org/)というパケット監視ソフトで調査してみました。
`Android`のパケット監視方法は色々あるようですが、今回は手軽に以下のような構成で通信を監視しました。
![プレゼンテーション1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684034/f349f4df-a7fc-1df3-f8f6-1b5060e2d8ce.png)
こうやってパケットを監視していると、公衆Wi-Fiなどが如何に危険かが身に沁みます。
一般人でも超手軽にパケットをのぞき見できるわけですから、平文通信なんて以ての外ですね。
未だに`HTTP`しか対応していない企業HPなども見かけますが、早急に対応してほしいものです。
無知なユーザー（決して貶める意ではありません）を脅威から保護するために最善の努力を行うのは、私たちホスト側の責務だと思います。

今回は調査目的で自分の通信を覗いていますが、くれぐれも他人の通信を覗いたり悪用することのないようにお願いします。

## プライベートDNSが`無効`のとき

PCなどのDNSクエリストリームも同様の動作となっていると思います。

![プレゼンテーション3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684034/868cf51b-ebea-3e37-1b56-9209bd7f9289.png)
スマホ（`192.168.137.149`）からGoogle Public DNS（`8.8.8.8`）に`query`が投げられ、Google Public DNSからスマホに`query response`としてIPアドレス（黒塗り部分）が返ってきています。
どのサイトにアクセスしようとしたのかが、第三者からでも見えることが確認できます。

- クエリとレスポンスが平文でやり取りされており、第三者から丸見え
- 一回の問い合わせで発生する通信は上りと下りの一往復

**問い合わせ**（`query`）と**応答**（`response`）で一セットになる標準的なDNSのやり取りで、**秘匿はされますが`DoT`でも同様の通信が行われます**。
`Google Public DNS`をPCのDNSサーバーに設定しているので`8.8.8.8`に問い合わせていますが、環境によってはISPのDNSサーバー等に投げていると思います。

DNSはスピード重視ですのでプロトコルには`UDP`が用いられています。
ステートレスなプロトコルですのでハンドシェイクなどは行われず、シンプルな通信が可能です。
仮にどこかでパケットが消えても再問合せすれば良いだけですので合理的だと思いますが、パケットが丸見えですので安全性もへったくれもありません。
特に無線通信ではプライバシーが侵害されたり、改竄されたりする可能性が高まります。
ちなみに、これらのDNSに関する通信の標準ポートは`53`です。

## `dns.google`を設定したとき

`Google Public DNS`で`Android 9`以降向けに紹介されている方法です。
`TSL`という暗号化方式を利用するにあたって、まずハンドシェイクという手順が行われます。

### ハンドシェイク

パケットに付したコメントはさほど正確でないのであくまでイメージだと思ってください。
![プレゼンテーション4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684034/29b76022-4204-31b3-3fac-51f7cf9d121b.png)
まず、`プライベートDNS`に入力できるのはホスト名のみなので、`dns.google`を解決する必要があります。
もともと`Google Public DNS (8.8.8.8)`がDNSサーバーなので分かりにくいですが、グレー部分より上の`8.8.8.8`はネットワークに設定されていたDNSサーバーで、下はスマホに設定された`dns.google`を解決した結果のDNSサーバーです。
この部分はセキュアではない、伝統的なDNSのやり取りです。

`dns.google`が解決されたら`TLS`のハンドシェイクが始まります。
詳しい説明は省きますが、DNSサーバーとクライアントの間で**公開鍵**や**乱数**をやり取りして**共通鍵**を作成し、以後はその共通鍵を用いて暗号化を行い通信します。

<details><summary>TCPとTLSに関する疑問点</summary>
`TCP`や`TLS`にそれほど詳しくないのですが、`TCP`は`3way ハンドシェイク`といって3回パケットをやり取りしなければ始まりません。
また、`TLS`は基本的に`Client Hello`というパケットが始まりの合図で、それは`TCP`の接続が確立した後に行われるはずです。
ここでは`3way ハンドシェイク`と`Client Hello`が同時に行われているように見えますが実際どうなっているのでしょうか。
他の場所では`3way ハンドシェイク`と`Client Hello`が分かれていたりと様々でした。
詳しい方がいましたらご教授下さい。
</details>

### DNSクエリストリーム

ハンドシェイクの後は以下のような通信が行われます。
通信内容は暗号化されているので付したコメントは想定ですが、仕組み上これで正しいと思われます。
![プレゼンテーション5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684034/dfe5fad3-8af3-ee9b-7694-2c8efe49c82d.png)
通信の内容が`TLSv1.2`で暗号化されているため、内容は`Application Data`と表示されています。
これで第三者はこれらの通信を傍受しても内容を確認することはできず、それぞれのパケットはハンドシェイク時の相手から送られてきていることが証明されていますので、一定の信頼性と機密性が保証されています。

**問い合わせ**（クエリ）と**応答**（レスポンス）がペアで行われている基本的な点は伝統的なDNSと変わりませんが、使用しているプロトコルが投げ捨ての`UDP`から、確認付きの`TCP`に代わったため`ACKパケット`が増えています。
この`ACKパケット`は必ず送られるものでもなく、効率化のためにバッファ分をまとめて送信することになっているので若干不規則な並びになっています。

`TCP Keep-Alive`というパケットが目を引きますが、これは`TCP`は一度接続が切れると再びハンドシェイクからやり直さなくてはならないため、一定期間通信が行われなかった時に切断されないよう送られるパケットです。

- 通信開始時にハンドシェイクという手続きが必要
- クエリとレスポンスが暗号化されており、第三者が確認できない
- 通信は、一回の問い合わせで発生する**上りと下り + `TCP`のパケット**

伝統的なDNSに比べて複雑になっているものの、`TCP`や`TLS`といった下層プロトコルに伝統的なDNSを載せているだけなので理解はし易いです。
一度ハンドシェイクが済んでしまえば、レイテンシーもそこまで増大せず安全に利用できるよくできた実装だと思います。

### 弱点

すこし話はずれますが`DoT`の弱点にも触れておきたいと思います。

あたりまえですが、`DoT`で保護されるのはDNSの通信のみで他の通信は暗号化されませんので`HTTP`なんかで接続すれば内容は丸見えになってしまいます。
加えて、`DoT`が保証するのは**クライアント⇔DNSサーバー**間の信頼性と機密性のみです。
[こちら](https://okuranagaimo.blogspot.com/2018/08/dnssecdns-over-tls.html)が詳しいですが、接続先のDNSサーバー自体が不正な場合などの安全性や、ホップ先の機密性は担保されていません。
`DoT`は`HTTPS`と同様にデジタル証明書で管理されますが、デジタル証明書は**偽でない**ことを証明するだけで接続先が安全であるか否かは関知しません。

利便性の面では、通信環境の悪いところなどでは`TCP`のコネクションが頻繁に切断され、ハンドシェイクが大量に行われ、それもなかなか上手くいかず…という感じでIPアドレスが取得できず接続エラーになってしまうことがあります。
自分も登山中に経験したことがあり、`プライベートDNS`を`無効`にすることで普通に接続できるようになりました。

また、個々の負荷増大は軽微でもDNSサーバーにとっては大きな負荷になるのかと思います。
基本的に`ACK`でパケットが1.5倍ですからネットワークトラフィックも増大しますし、暗号化と復号化の処理も負荷になりそうです。
それでも（無料で）安定した`Public DNS`を提供して下さっているのですから、関係各社様には頭が上がりません。

`DoT`で保護されるのはDNSサーバーとクライアント間の通信のみであることは前述のとおりですが、ハンドシェイク前のホスト名解決が伝統的なDNSである点が気になりました。
`dns.google`に対して偽のIPアドレスを解決すれば以後の通信が不正になるのではと思いましたが問題ないのでしょうか？
とはいえ、なりすましやスコープ外の危険性は`DoT`に固有のものではありませんので、導入を否定する要因にはなりません。

いたちごっこなのはセキュリティの性ですが、適用しておくべきプロトコルだと思います。

## 自動を設定したとき

[Google Public DNS](https://developers.google.com/speed/public-dns/docs/using#android)によると、`自動`に設定した場合は、まず`853`ポートへの接続を試行し、成功したら`DoT`で、失敗したら`53`ポートへ伝統的なDNSで問い合わせを行うようです。

当方の環境では`dns.google`を明示した時と同じでした。
![google_public_dns_auto.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684034/a6acc62c-36a5-3886-6e0e-23c88a1d1e41.png)
DNSサーバーが既定のままだったり、コネクションがうまくいかなかったりすると伝統的なDNSで問い合わせが行われると思われます。

一度、`自動`に切り替えた後`TCP`の接続に失敗して伝統的なDNSでやり取りしていた時がありました。
手動で再接続したら`DoT`に切り替わりましたが、どうやら`DoT`に執着するよりも接続性を優先する実装のようです。

## `one.one.one.one`を明示した時

自称最速DNSサーバー、Cloudflareの`1.1.1.1`です。
前述の通り設定できるのはホスト名のみなので`one.one.one.one`と設定します。

![cloudflare_auto.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684034/1e0cb042-2fa7-7b0b-1ad5-d139e24f9905.png)

こちらも`DoT`で秘匿された通信ができています。

## `OpenDNS`を明示した場合

国内にサーバーがあり優秀な部類に入ると思われる`OpenDNS`ですが、`DoT`には対応してなさそうです。
ホスト名を`nslookup`で検索して`プライベートDNS`に明示してみましたが、`853`ポートへアクセスできませんでした。
パケットを監視しているとエラーのパケットが返ってきているのが見えますが、スマホからはIPアドレスが取得できない状態（=**通信不可**）が見えるだけです。
対応していない（または存在していない）ホスト名を打ち込むと通信ができなくなるのでよく確認する必要がありますね。

# おまけ（Windows）

Windowsでは`DNS over HTTPS`（以下`DoH`）という技術でDNS通信を暗号化しようとしています。
これは`HTTPS`の`443`ポートを用いてDNSを行うもので、`UDPパケット`と`TCPパケット`の両方が用いられます。
2020年9月10日時点ではOSレベルの標準実装は行われていませんが、Chromeなどのブラウザは`DoH`でDNSの問い合わせを行っています。
![プレゼンテーション6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684034/d4517a6b-fc43-7fb4-f95f-f605536b9280.png)
このように伝統的なDNSの問い合わせと`DoH`が混在していて、ブラウザから投げられる`DoH`は`UDPプロトコル`を利用しています。
`DoH`でもホスト名解決から始まるのは変わらないんですね。

ちなみに`Windows insider Program (Dev Channel)`に登録していて`Build 19628`以降がインストールされていれば、レジストリを弄ることでOSレベルでも`DoH`を適用することができます。
![プレゼンテーション7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684034/27ac3797-a047-e49a-b585-262c9d436388.png)
基本的に全ての通信が`DoH`で行われているのが確認できます。
`nslookup`はDNSサーバーを明示している扱いなので`DoH`の適用から外れているのだと思われます。
ブラウザレベルでの実装と違って`TCP`と`TLSv1.2`が用いられているのが面白いですね。
`Dev Channel`はだいぶ不安定になりますが気になる方は是非自己責任でどうぞ。
設定方法は[公式ブログ](https://techcommunity.microsoft.com/t5/networking-blog/windows-insiders-can-now-test-dns-over-https/ba-p/1381282)を参照してください。

# Windows 11
Windows 11ではすべてのチャネル利用者に`DoH`の機能が提供されています。
簡単にDNS通信を暗号化できるので是非皆様ご利用ください。

まずは以下のページに移動してください。
`設定`->`ネットワークとインターネット`->`Wi-Fi`->`ハードウェアのプロパティ`

次に、`DNSサーバーの割り当て`という項目がありますので、選択して`手動`に変更します。

`IPv4`と`IPv6`をそれぞれ`オン`にして、`優先DNS`と`代替DNS`を設定します。
例として`Google Public DNS`を利用します。

`IPv4`には`8.8.8.8`と`8.8.4.4`を入力したら、`優先DNS暗号化`と`代替DNS暗号化`を`暗号化のみ(HTTPS経由のDNS)`に変更します。
`IPv6`の方は、`2001:4860:4860::8888`と`2001:4860:4860::8844`を設定し同様に暗号化の設定を行います。

保存して`DNSサーバー`に`(暗号化されています)`と表示されれば完了です。
全ての`Wi-Fi`接続に適用されます。

# 所感

悪意ある第三者は虎視眈々と我々の通信を狙っています。
コロナ禍で社内だけだったビジネス関係の通信環境も多種多様になったことと思いますが、セキュリティ対策まで手が回りにくいのも実情かと思います。
Wi-Fiやファイアウォール、VPNなど、セキュリティは多角的にアプローチする必要がありますが、全ての通信の起点といっても過言ではないDNSを意識することはあまりありませんでした。
手軽に、どこでも使えるスマートフォンだからこそ、少しでも安全な通信ができるよう適用しておきたい「プライベートDNS」、皆様もこの機会に是非設定してみてはいかがでしょうか。