プロキシを設定し、IP照会ページを開くと、プロキシのIPが表示されました。これで完了でしょうか。必ずしもそうとは限りません。ページは、WebRTCを通じて本当のIPを見つけられる場合があります。WebRTCは、ブラウザのプロキシ設定を仕様上無視するブラウザ機能です。レジデンシャルプロキシを使って現地の訪問者としてサイトを見ている場合、WebRTCリークがあると、目に見える兆候もなくその効果が失われます。
このガイドでは、リークが起きる仕組み、2分でできるテスト方法、そしてChrome、Firefox、自動化ブラウザでリークを塞ぐ方法を説明します。
リークが起きる仕組み
WebRTCを使うと、ブラウザどうしが通話したりデータを直接送り合ったりできます。そのために各ブラウザは「ICE候補」、つまり自分に到達できる可能性のあるアドレスを集めます。その一環として、UDPでSTUNサーバーに「あなたから見て、私はどのパブリックアドレスから来ていますか?」と問い合わせます。その答えは候補に格納され、ページのJavaScriptから読み取れます。
問題は、この問い合わせが通る経路にあります。一般的なHTTPプロキシが運ぶのはウェブリクエストだけで、UDPは運びません。そのためSTUNリクエストは自分の回線から出ていき、返ってくる答えは本当のパブリックIPになります。WebRTCのアドレス処理に関するIETF標準であるRFC 8828は、これをはっきり述べています。直接のインターネットアクセスが許可されている場合、STUNによる確認はプロキシを迂回し、クライアントのパブリックIPを明らかにすることがあります。また、通話品質を考慮して、WebRTCの通信を既定ではプロキシ経由にしないことも推奨しています。
重要な点が2つあります。
- ローカルアドレスは現在ほとんど隠されています。 ChromeとFirefoxは、ほとんどのページでプライベートネットワークアドレスをランダムな
.local名に置き換えます。Firefoxではmedia.peerconnection.ice.obfuscate_host_addressesという設定がこれにあたり、標準でオンです。本当に危険なのはパブリックIPです。 - ページに許可は必要ありません。 候補の収集にはカメラやマイクへのアクセスが不要なので、確認のプロンプトも出ません。
2分でテストする
外部のテストサイトは必要ありません。プロキシをオンにした状態で、確認したいページ(どのページでも構いません)を開き、開発者コンソール(F12)を開いて、次のコードを貼り付けます。
const pc = new RTCPeerConnection({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }] });
pc.createDataChannel("t");
pc.onicecandidate = (e) => e.candidate && console.log(e.candidate.candidate);
pc.createOffer().then((o) => pc.setLocalDescription(o));
typ srflx を含む行を探してください。その行のアドレスは、STUNサーバーに見えたパブリックIPです。
- 自宅またはオフィスのIPと一致する場合、WebRTCがリークしています。
- プロキシのIPと一致する場合、または
srflxの行がまったくない場合は、問題ありません。 typ hostと.local名を含む行は、隠されたローカルアドレスです。これは想定どおりの表示です。
次に、同じブラウザで IP Info などのIP照会サービスと比較します。プロキシのIPと場所が表示されるはずです。照会結果にはプロキシが表示されるのに srflx の行に自分のIPが出ている場合は、プロキシは機能していて、WebRTCがそれを迂回しています。
Chromeで対処する
最も簡単な方法:管理してくれる拡張機能を使う。 Shifterブラウザ拡張機能は、接続中はWebRTCリーク対策を標準でオンにします。内部的には、Chrome拡張機能は webRTCIPHandlingPolicy というプライバシー設定でこれを制御しています。別の拡張機能がすでにWebRTCを制御している場合、有効にできるのはどちらか一方だけで、保護が有効でないときはShifter拡張機能がその旨を通知します。もう一方の拡張機能は削除するか無効にしてください。
管理対象のコンピューター: Chromeには WebRtcIPHandling というエンタープライズポリシーがあり、4つの値があります。
| 値 | WebRTCが使うもの |
|---|---|
default | 利用可能なすべてのネットワークインターフェース |
default_public_and_private_interfaces | 外部向けインターフェース(プライベートアドレスも許可) |
default_public_interface_only | 外部向けインターフェースのみ(プライベートアドレスなし) |
disable_non_proxied_udp | プロキシ経由のTCP。UDPはプロキシが対応している場合のみ |
プロキシ使用時のパブリックIPリークを止められるのは disable_non_proxied_udp だけです。「パブリックインターフェース」の2つの値では、自分の回線からSTUNサーバーに問い合わせる動作が残ります。
Firefoxで対処する
FirefoxでもShifter拡張機能が対処します。手動で行う場合は、about:config を開いて次のように設定します。
media.peerconnection.ice.proxy_only_if_behind_proxyをtrueにします。プロキシが設定されている場合にのみ、WebRTCがプロキシを使います。これがピンポイントな対処方法です。media.peerconnection.ice.default_address_onlyをtrueにします。デフォルトの経路だけが使われます。余分なローカルアドレスは隠れますが、これだけではパブリックIPの漏えいは防げません。media.peerconnection.enabledをfalseにします。WebRTCを完全にオフにします。最も強引な方法で、そのブラウザでは通話が使えなくなります。
そのあと、コンソールのテストをもう一度実行してください。srflx の行が消えるか、プロキシのIPが表示されていれば問題ありません。
自動化ブラウザで対処する
Playwright、Puppeteer、Seleniumは実際のブラウザを起動するため、同じようにリークします。Chromiumの場合は、次のオプションを付けて起動してください。
--force-webrtc-ip-handling-policy=disable_non_proxied_udp
Firefoxの場合は、フレームワークが起動するプロファイルに、同じ about:config の設定を行います。そのうえで、上のコンソールテストをスクリプトから実行し、srflx の結果をセットアップ確認の一部としてチェックしてください。これらのオプションを指定する場所は、SeleniumとPlaywrightでのレジデンシャルプロキシ設定のガイドで紹介しています。
リークはWebRTCだけではありません
この機会に、関連する抜け穴も塞いでおきましょう。
- DNS。 名前解決がプロキシを迂回し、自分のネットワークから行われることがあります。
socks5hと確認方法については、DNSリークの防止のガイドで解説しています。 - IPv6。 プロキシの経路がIPv4のみの場合、IPv6も使えるブラウザが一部のリクエストでIPv6を使うことがあります。
- タイムゾーンと言語。 これらはIPを明かすものではありませんが、プロキシの所在地と矛盾することがあります。
まとめ
WebRTCリークが起きるのは、ブラウザがUDPでSTUNサーバーに自分のアドレスを問い合わせる一方で、ほとんどのプロキシがウェブ通信しか運ばないためです。コンソールのコードでテストしてください。srflx のアドレスが自分のものであれば、ページからも見えています。Shifter拡張機能でリーク対策をオンにするか、Chromeでは disable_non_proxied_udp、Firefoxでは proxy_only_if_behind_proxy を設定してから、もう一度テストしてください。