はじめに(この記事で分かること)#

VPNに接続し、確認サイトでIPアドレスがVPNのものに変わったのを見て安心する。ところが同じ画面の下の方に、契約しているISPの名前が入ったDNSサーバーが並んでいることがある。通信の中身はトンネルを通っているのに、「どのサイトを開こうとしたか」という問い合わせだけが外に出ている状態で、これをDNSリークと呼ぶ。

この記事では、名前解決の経路がどこで分かれるのかをRFCとUbuntu 26.04のmanページで確かめ、systemd-resolvedでの確認手順、ブラウザのセキュアDNSやウイルス対策ソフトが原因になる仕組み、確認サイトの結果の読み方を順に扱う。リークを塞いでも匿名になるわけではない。その点は最後にまとめて書く。

前提: 脅威モデルの整理#

脅威モデル入門では、隠す相手をISP、プラットフォーム、広告網・データブローカー、法執行機関、標的型攻撃者の5種類に分けた。DNSリーク対策が効く相手は、このうちかなり限られる。

もっとも直接に効くのはISPと、同じネットワークにいる第三者だ。RFC 9076(DNS Privacy Considerations、2021年7月)は、DNSの通信のほとんどが暗号化されずに送られており、暗号化されていない経路ではほかの通信と同じように盗聴者から見えると書いている。Chromiumのブログ(2020-05-19)も、暗号化されていないDNSでは同じネットワークにつながった攻撃者がほかの利用者の閲覧の傾向を観察できると説明している。公衆Wi-Fiの運営者や同じ回線にいる誰かは、ここに含まれる。

一方、プラットフォームと広告網に対しては、DNSリークの有無はほとんど関係しない。彼らが見ているのはログインしたアカウントや、Cookie、ブラウザの特徴だからだ(ブラウザフィンガープリント)。法執行機関については、ISPのDNSサーバーに問い合わせ記録がどれだけ残り、手続きでどこまで取得されうるかを一次資料で確認できなかったので、この記事では効果を見積もらない。

もう一つ、見落とされやすい相手がいる。DNSの問い合わせを受け取る側のリゾルバの運営者だ。RFC 9076は、権威サーバーと違ってリカーシブリゾルバは利用者のDNSの通信をすべて見ると書いている。リークを塞ぐとは、問い合わせの宛先をISPからVPN事業者に移すことであって、誰にも見られなくすることではない。

DNSリークが起きる仕組み#

ブラウザにexample.comと打ち込むと、端末はまずそのドメイン名に対応するIPアドレスを調べる。RFC 1034(1987年)は、端末側の解決機能を最小限にし、再帰的な問い合わせを引き受けるネームサーバーに処理を任せる形を「スタブリゾルバ」として説明している。スタブに必要なのは、再帰的な問い合わせを代行してくれるネームサーバーのアドレスの一覧だけだ。そしてRFC 1035は、ネームサーバーへの問い合わせにUDPとTCPの53番ポートを使うと定めている。

VPNが守れるのは、トンネルを通った通信だけだ。端末全体のVPNに接続すると、通常はVPN事業者が指定するDNSサーバーの一覧が端末に登録される。ところが、端末が持っているDNSサーバーの一覧はそれだけではない。家庭のWi-FiのDHCPで配られたルーターやISPのDNSサーバーも、物理インターフェースの設定として残っている。どの問い合わせをどのサーバーに送るかの判断が、VPN側ではなくISP側のサーバーに傾いたとき、問い合わせはトンネルの外を53番ポートの平文で出ていく。

Mullvadのヘルプ(2026-06-24更新)は、さらに広くDNSリークを定義している。Mullvad以外のDNSサーバーを使っている状態がDNSリークだ、という定義である。この定義だと、問い合わせがトンネルの中を通っていても、ブラウザが独自にCloudflareなどのDoHサーバーへ問い合わせていればリークに数えられる。経路の問題(ISPに見えるか)と、宛先の問題(誰が問い合わせを集めるか)は分けて考えたほうがよい。前者はISPと同じネットワークの第三者に対する問題で、後者はVPN事業者とは別の事業者に閲覧先の記録を渡す問題だ。

原因は大きく四つの層に分かれる。OSが複数のDNSサーバーのどれを使うかの判断。ブラウザが持つ独自のDNS機能。拡張機能やウイルス対策ソフトがDNSを横取りする挙動。そしてIPv6のように、トンネルが運ばない経路の存在だ。Mullvadのヘルプは、ブラウザのセキュアDNS、DNS設定を変える拡張機能、AvastやAVG、F-Secure、Portmasterといったセキュリティソフト、macOSのLittle Snitchの「DNS Encryption」機能、AndroidのプライベートDNSを、具体的な原因として挙げている。

Windowsには、OS自体の挙動として知られた原因がある。Microsoftのポリシー文書(ADMX_DnsClient、2026-10-14)は「Smart Multi-Homed Name Resolution」について、複数のネットワークに接続した端末が、すべてのネットワークに対してDNS・LLMNR・NetBTの問い合わせを並列に出して名前解決を最適化する設定だと説明し、ポリシー「Turn off smart multi-homed name resolution」を有効にすればこの最適化を止められるとしている。Microsoftの文書自体はVPNに触れていない。これがVPN利用時のリークにつながるという説明は、VPN事業者のOVPNのブログ(2015-10-19)によるもので、通常のインターフェースに設定されたDNSサーバーと、VPNが指定する別のDNSサーバーの両方に問い合わせが出るため、リークの危険があるとしている。

Ubuntu 26.04での確認と対策#

ここからは基準OSのUbuntu 26.04 LTSでの話になる。Ubuntu Serverのドキュメントは、Ubuntuの既定のスタブリゾルバはsystemd-resolvedで、127.0.0.53の53番ポートで待ち受け、/etc/resolv.confにそのアドレスがnameserver 127.0.0.53として書かれていると説明している。ただしこのページには対象リリースの明記がなく、例示の出力はUbuntu 24.04のものだ。そこで、26.04(resolute)のmanページで挙動を確かめた。systemd-resolved.service(8)は、/etc/resolv.confの扱いに四つの方式があるとし、/run/systemd/resolve/stub-resolv.confへのシンボリックリンクにする方式を推奨している。この方式では、アプリケーションは127.0.0.53のスタブに問い合わせ、実際にどの上流サーバーへ送るかはsystemd-resolvedが決める。

まず現状を見る#

最初に、/etc/resolv.confが何を指しているかと、systemd-resolvedがインターフェースごとにどのDNS設定を持っているかを確認する。

ls -l /etc/resolv.conf
resolvectl status

/etc/resolv.confがstub-resolv.confへのリンクなら、上の推奨方式で動いている。resolvectl(1)によれば、statusは現在有効なグローバルとインターフェースごとのDNS設定を表示する。VPN接続中にこの出力を見ると、物理インターフェース(例: enp1s0やwlp2s0)とVPNのインターフェース(例: wg0)が別々の項目として並び、それぞれにDNSサーバーとドメインの設定がある。インターフェースを絞るなら次のようにする。

resolvectl dns wg0
resolvectl domain wg0

問い合わせの振り分け規則と分割DNS#

リークの判断に必要なのは、systemd-resolvedが問い合わせをどのインターフェースに送るかの規則だ。systemd-resolved.service(8)の「PROTOCOLS AND ROUTING」の節は、次のように説明している。インターフェースごとに「ルーティングドメイン」を持てる。検索ドメインも、先頭に~を付けたルート専用ドメインも、どちらもルーティングドメインとして扱われる。問い合わせる名前がいずれかのルーティングドメインに合致すれば、ラベル数がもっとも多い(もっとも長く一致する)ドメインを持つインターフェースのDNSサーバーに送られる。どれにも合致しない問い合わせは、デフォルトルートとして構成されたすべてのインターフェースのDNSサーバーに送られる。同じ条件のインターフェースが複数あれば、問い合わせは並列に送られ、最初に返ってきた成功の応答が使われる。

リークは最後の規則から生まれる。VPNのインターフェースにも物理インターフェースにもデフォルトルートの役割が残っていると、合致するドメインのない普通の問い合わせが、VPNのDNSサーバーとISPのDNSサーバーの両方に並列で飛ぶ。これを防ぐ手段としてmanページが示すのが、VPN側のインターフェースに~.というルート専用ドメインを設定する方法だ。~.はすべての名前に合致するので、同じ~.を持たないほかのインターフェースは、これらの問い合わせの送り先として検討されなくなる。

ここに分割DNSの落とし穴がある。~.は「いちばん短い一致」でもある。物理インターフェースにDHCPで検索ドメイン(例えばルーターが配る家庭内向けのドメイン)が設定されていると、そのドメインに属する名前は、~.よりラベル数の多いドメインに一致するため、物理インターフェース側のDNSサーバーに送られる。社内ネットワーク向けのVPNで~corp.exampleのような限定的なルーティングドメインだけを設定している場合はもっと分かりやすく、それ以外のすべての問い合わせが元の回線に残る。これは分割DNSとして意図された動作であり、匿名化を目的とするVPNの使い方とは目的が逆だ。resolvectl statusで物理インターフェース側に検索ドメインが残っていないかを見るのは、このためだ。

wg-quickのDNS=はどう効くか#

WireGuardをwg-quickで使う場合、設定ファイルの[Interface]節にDNS=を書く。

[Interface]
PrivateKey = (秘密鍵)
Address = (VPN事業者が割り当てるアドレス)
DNS = (VPN事業者が指定するDNSサーバーのアドレス)

wg-quick(8)(wireguard-tools 1.0.20250521、Ubuntu 26.04)によれば、DNS=に書いたアドレスはインターフェースのDNSサーバーとして設定され、起動時にresolvconf -a tun.<インターフェース名> -m 0 -xが、停止時にresolvconf -d tun.<インターフェース名>が実行される。

Ubuntu 26.04では、このresolvconfはsystemd-resolvedのものになる。パッケージ情報では、systemd-resolvedパッケージが/usr/sbin/resolvconfを含んでいる。resolvectl(1)は、resolvectlがresolvconfという名前で呼ばれたとき(通常はその名前のシンボリックリンク経由)に互換動作をすると説明し、-x(排他的な動作)は部分的にしか対応しておらず、~.という検索ドメインを追加する扱いに置き換えると書いている。-mは対応しておらず黙って無視される。つまりDNS=を書いておけば、前節の~.がWireGuardのインターフェースに付く。接続後に次のコマンドで確かめられる。

resolvectl domain wg0

ここに~.が出ていれば、合致するドメインのない問い合わせはwg0側に集まる。別のresolvconf実装(openresolvなど)を入れている環境では、この対応関係が成り立たない可能性がある。本稿ではその構成を検証していない。

DNS=を書かずに接続した場合や、自前のスクリプトでインターフェースを作った場合は、~.が付かない。wg-quick(8)は、上のresolvconfの呼び出しが望ましくなければPostUpとPostDownを代わりに使えるとしている。PostUpなどの中では%iがインターフェース名に置き換わる。resolvectl(1)のdnsとdomainのコマンドを使えば、例えば次のように書ける。

PostUp = resolvectl dns %i (VPN事業者のDNSサーバー); resolvectl domain %i '~.'

NetworkManagerで接続している場合#

デスクトップ版でNetworkManagerにVPNの設定を取り込んでいる場合は、NetworkManager側の優先度が効く。NetworkManager.conf(5)(network-manager 1.54.3)は、dnsキーが未指定でも、/etc/resolv.confがstub-resolv.confなどsystemd-resolvedのファイルへのリンクであれば自動的にsystemd-resolvedが選ばれ、NetworkManagerはDNS設定をsystemd-resolvedに渡すと書いている。

nm-settings-nmcli(5)のipv4.dns-priorityの説明は、数値が小さいほど優先され、未設定(0)の場合はWireGuardを含むVPNが50、そのほかの接続が100になるとしている。負の値には、それより大きい数値の設定を除外する特別な効果がある。そして同じ説明の中で、すべてのDNSリークを避けるには、使うべきプロファイルの優先度をもっとも小さい負の値にするよう書いている。設定例は次のとおりで、<VPN接続名>はnmcli connection showで表示される名前に置き換える。

nmcli connection modify <VPN接続名> ipv4.dns-priority -50 ipv6.dns-priority -50
nmcli connection up <VPN接続名>

IPv6側のipv6.dns-priorityにも、同じmanページに同じ内容の説明(負の値の除外効果、VPNの既定値50、DNSリーク回避の記述)がある。設定後は、接続し直してからresolvectl statusで物理インターフェース側のDNSサーバーが使われていないかを確かめる。

パケットで確かめる#

設定を読んだだけでは、実際に何が出ていっているかは分からない。いちばん確実なのは、物理インターフェースを流れるパケットを見ることだ。tcpdump(8)の-iは観測するインターフェースを、-nはアドレスやポート番号を名前に変換しないことを指定する。

sudo tcpdump -n -i enp1s0 'port 53 or port 853'

VPNに接続した状態でこれを動かし、別の端末画面でいくつかのサイトを開く。何も表示されなければ、平文DNS(53番)とDNS over TLS(RFC 7858が定める853番)は物理インターフェースから出ていない。表示されれば、その宛先がリークしている先だ。ただし、この方法でDoHは見分けられない。RFC 8484のDoHは普通のHTTPS通信として送られるため、ポート番号でほかの通信と区別できない。DoHの有無はブラウザの設定で確かめることになる。

最後に、VPNが切れた瞬間の話がある。トンネルが落ちれば、wg0側の~.もなくなり、問い合わせは物理インターフェースのDNSサーバーに戻る。これはDNSの設定では防げず、キルスイッチの役割になる。キルスイッチの解説で扱う。また、MullvadのヘルプはLinuxについて、IPv6のDNSアドレスが表示される場合は、アプリでIPv6を有効にするか、Linux側でIPv6を無効にするよう案内している。トンネルがIPv4しか運ばない構成では、IPv6の経路が別に残りうる。

ブラウザとアプリの側#

OSの設定を整えても、アプリケーションがOSのリゾルバを使わなければ意味がない。その代表がブラウザの暗号化DNSだ。

Firefoxについて、Mozillaのサポート記事(2026-10-07更新)は保護レベルを四つ説明している。「既定の保護」は、提供地域で自動的にセキュアDNSを有効にし、問題があれば既定のリゾルバに戻る。そして、VPN、ペアレンタルコントロール、企業ポリシーが有効なとき、またはネットワークがセキュアDNSを使わないよう伝えてきたときは、DoHを無効にする。「保護強化」と「最大保護」と「カスタム」は、選んだプロバイダーのDoHを常に使う。最大保護は、セキュアDNSのリゾルバに接続できないと警告を出す。もう一つの記事(2026-10-01更新)によると、DoHを既定で有効にした地域は、米国(2019年)、カナダ(2021年)、ロシアとウクライナ(2022年3月開始)で、ほかの国にも広げる作業中だという。

この仕様から読めることを整理する。既定の保護のままなら、Firefoxは自分でVPNを検知した場合にDoHを止め、OSのリゾルバに任せる。その場合の問い合わせは、前節のsystemd-resolvedの規則に従う。保護強化や最大保護で特定のプロバイダーを選んでいると、VPN接続中でも問い合わせはそのプロバイダーに送られる。端末全体のVPNを使っていれば、その通信自体はトンネルの中を通るので、ISPからは見えない。ただしMullvadの定義ではリークに数えられ、閲覧先の記録はVPN事業者とは別のDoH事業者に集まる(ここまでは仕様からの推論)。

Chrome系のブラウザについては、Chromiumのブログ(2020-05-19、Chrome 83での導入時)が仕組みを説明している。Chromeは、DoHに対応したDNSプロバイダーの一覧を持ち、利用者が今使っているDNSプロバイダーがその一覧にあれば、同じプロバイダーのDoHに自動で切り替える。プロバイダーは変えない。DoHの接続に問題があれば、既定では同じプロバイダーの通常のDNSに戻る。Windowsのペアレンタルコントロールが有効な場合と、企業ポリシーで管理された環境では、セキュアDNSを無効にする。設定画面で特定のDoHプロバイダーを選ぶと、通常のDNSに戻らないモードになる。

ここから推論すると、自動のモードではChromeの問い合わせ先はOSが使っているDNSプロバイダーのままなので、OS側でVPNのDNSに向いていれば、それに従うことになる。プロバイダーを明示的に選んだ場合は、Firefoxの保護強化と同じ構図になる。なお、このブログは2020年時点でLinux版での提供を「近日」としており、2026年のLinux版Chromeでの既定の動作は、今回Googleの公式文書で確認できなかった。Linux版は自分の画面の設定で確かめてほしい。

拡張機能も同じ構図だ。MullvadのヘルプはDNS設定を変える拡張機能の例としてCIRA Canadian Shieldを挙げ、無効にするよう案内している。ウイルス対策ソフトやセキュリティソフトは、もっと低い層でDNSを横取りすることがある。Mullvadは、AvastとAVGは特定のシールドを無効にすること、F-SecureやPortmasterはアンインストールが必要になる場合があることを書いている。これらのソフトは利用者を守るためにDNSを検査しているので、無効にすれば別の保護を失う。どちらを取るかは脅威モデル次第だ。

AndroidのプライベートDNS(DNS over TLS)と、Windows 11のDoH・DoTの設定も、Mullvadのヘルプが原因として挙げている。Windows 11についてはnetsh dns add globalのコマンドで無効にし、netsh dns show globalで確かめる手順が案内されている。

ルーターでVPNを終端している場合#

ネットワーク機器の記事のように、ルーターでVPNを終端し、家全体のDNSをルーターに集めている場合も、ここまでの話はそのまま当てはまる。ルーターが強制できるのは、53番や853番のような識別できる経路だけだ。同記事にあるとおり、HTTPSの443番を使うDoHはポート番号では止められない。ブラウザが独自のDoHプロバイダーを使えば、問い合わせはルーターのトンネルの中を通りつつ、ルーターのDNS設定とは別の事業者に届く。端末のVPNとルーターのVPNを重ねている場合も、確認すべき点は同じで、最終的にどのリゾルバが問い合わせを受けているかだ。Mullvadのヘルプも、ルーターでMullvadに接続している場合はルーター向けガイドのトラブルシューティングを確認するよう案内している。

確認ツールの読み方と限界#

ブラウザで使える確認サイトは、リゾルバの運営者を特定する仕組みを持っている。ipleak.netのAPIの説明では、セッションごとに固定の文字列と、問い合わせごとに新しいラベルを組み合わせたサブドメイン({session}-{label}.ipleak.net)に問い合わせを出させる。こうした一度きりの名前はキャッシュに残っていないので、必ず誰かのリゾルバが確認サイト側の権威サーバーへ問い合わせに来る。確認サイトは、問い合わせに来たリゾルバのIPアドレスを記録して画面に表示する(ここでの仕組みの説明は、APIの記述からの推論を含む)。

代表的な確認サイトと運営者は次のとおりだ。いずれもVPN事業者と関係がある。

確認サイト運営者・関係確認した根拠
Mullvad Connection check(mullvad.net/check)VPN事業者のMullvadMullvadのヘルプが自社の確認ツールとして案内
ipleak.netVPN事業者のAirVPNページ下部に「powered by」とAirVPNのロゴ、サポートはAirVPNのフォーラム
dnsleaktest.comVPN事業者IVPNと関係があるページ下部のプライバシーポリシーのリンクに「IVPN Limited」の表記。サイト上に運営者の明示はない

結果の読み方は、Mullvadのヘルプが分かりやすい。Mullvadの確認ツールは、リークがなければ緑、テストが失敗すれば黄、リークしているDNSサーバーがあれば赤で表示し、赤の場合はそのIPアドレスの持ち主をMaxMindで調べるよう案内している。ほかのサイトでも、表示されたDNSサーバーのISP名や組織名が、自分の契約しているISPや、VPN事業者とは別の事業者(Cloudflare、Googleなど)になっていないかを見る。VPN事業者が上流で別のリゾルバを使っている場合には、VPN事業者以外の名前が出ても、それが自分の端末からのリークとは限らない(推測)。迷ったら、前節のtcpdumpで自分の端末から何が出ているかを確かめるほうが確実だ。

確認サイトの限界は三つある。一つ目に、確認サイトは、自分の今のIPアドレスと、自分が使っているリゾルバを、そのVPN事業者に見せる行為でもある。VPNの外でこのテストをすれば、本当のIPアドレスを見せることになる。上の三つはどれもVPN事業者と関係があるので、確認サイトを開いた記録がどう扱われるかは各社の方針次第だ。dnsleaktest.comのプライバシーポリシーは、アクセス解析にMatomoを使い、IPアドレスの下位2オクテットを直ちに破棄すると書いているが、DNSテスト中の記録の扱いには触れていない。

二つ目に、ブラウザで行うテストは、そのブラウザの名前解決の経路しか調べない。ブラウザがDoHを使っていれば、OS側の設定が漏れていても結果には出ないし、逆も起こる。メールソフトや同期クライアントなど、ブラウザ以外のアプリの問い合わせは、tcpdumpのようにOS側で見るしかない。

三つ目に、テストはその瞬間の状態しか示さない。Wi-Fiを切り替えた直後、スリープからの復帰直後、VPNの再接続中といった、設定が組み替わる瞬間に漏れていても、落ち着いてから実行したテストには出てこない。匿名性の自己点検の手順でWebRTCやIPv6と合わせて定期的に確認し、ネットワークを変えたときにもう一度確かめる運用が現実的だ。WebRTCの確認はWebRTCリークの記事で扱う。

限界と残るリスク#

DNSリークを塞いで得られるのは、名前解決の問い合わせがISPや同じネットワークの第三者に平文で見えなくなること、それだけだ。

問い合わせは、なくなったのではなくVPN事業者のリゾルバに移った。RFC 9076が書くとおり、リカーシブリゾルバは利用者のDNSの通信をすべて見る。そして、暗号化された経路を使っても、リカーシブリゾルバが手にするデータは減らない。VPN事業者をどこまで信頼できるかは、ノーログVPNの記事で扱った監査や押収の実績で判断するしかない。ブラウザで別のDoHプロバイダーを選べば、その信頼の対象が一社増える。

DNSを塞いでも、接続先のIPアドレスそのものはVPN事業者に見える。VPNの出口から先では、接続先のサイトが出口のIPアドレスを見る。サイト側から見たとき、Cookie、ログイン中のアカウント、ブラウザフィンガープリントは、DNSの設定と無関係に残る。WebRTCのように、DNSとは別の経路でIPアドレスが漏れる仕組みもある。

対策そのものにも穴がある。systemd-resolvedの~.は、より長く一致する検索ドメインに負ける。NetworkManagerの優先度は、NetworkManagerを通らない接続には効かない。ブラウザやアプリが独自のリゾルバを持てば、OSの設定はすべて迂回される。VPNが切れた瞬間の問い合わせは、キルスイッチがなければ物理回線に戻る。確認サイトで緑が出ても、それはその瞬間のそのブラウザの経路が合格したという意味にとどまる。

そして、DNSを含めて通信の層を整えても、匿名になれるわけではない。IPアドレスで個人は特定できるかで書いたように、IPアドレスとDNSは特定の経路の一部にすぎない。VPN全体の位置づけはVPN完全ガイドとVPNの匿名性レベルにまとめてある。

まとめ#

VPNを初めて使う人なら、今日できることは一つでいい。VPNに接続した状態で、使っているVPN事業者の確認ページ(Mullvadならmullvad.net/check)か、ipleak.netを開き、表示されたDNSサーバーの組織名が自分のISPになっていないかを見る。ISPの名前が出たら、まずブラウザのセキュアDNSの設定と、DNSを扱う拡張機能やセキュリティソフトを疑う。Firefoxなら設定のDNS over HTTPSの欄が「既定の保護」かどうかを確かめる。

Ubuntu 26.04でWireGuardやNetworkManagerを使っている開発者なら、resolvectl statusでVPNのインターフェースに~.が付いているか、物理インターフェースに検索ドメインが残っていないかを確かめ、sudo tcpdump -n -i <物理インターフェース> 'port 53 or port 853'で、VPN接続中に平文の問い合わせが外に出ていないことを一度は自分の目で見ておく。wg-quickの設定ファイルにDNS=を書き忘れていないかも、このときに分かる。

DoHのプロバイダーをブラウザで選んでいる人は、その事業者に閲覧先の記録を渡す判断をしていることを意識しておく。VPN事業者とDoH事業者のどちらに預けるかを、一社に絞るか二社に分けるかは、両社への信頼の度合いで決めることになる。

出典#

参照日はいずれも2026-11-08。