1. はじめに
モバイルまたはクロスプラットフォームのアプリケーションでは、ローカル DNS ハイジャックやキャッシュ汚染などの問題を回避するために HTTPDNS がよく使用されます。しかし、すべてのネットワークライブラリが統合のための DNS Hook インターフェイスを提供しているわけではありません。直接 IP 接続は、リクエストの詳細しか変更できないシナリオでの回避策です。まず、HTTPDNS から解決結果を取得します。次に、直接 IP アドレスを使用するように URL を書き換えます。また、TLS ハンドシェイクとルーティングが元のドメイン名を引き続き使用するようにする必要があります。
たとえば、https://www.example.com は https://1.2.3.4 になりますが、リクエストヘッダーは Host: www.example.com を保持します。
このトピックでは、直接 IP 接続を必要とする典型的なシナリオに焦点を当てます。このアプローチの基本原則を理解するのに役立つように、条件、一般的な問題、および解決策について説明します。
2. 直接 IP 接続を使用する状況
ネットワークライブラリに対する制御レベルを判断して、直接 IP 接続を有効にするかどうかを決定します。
DNS Hook または Host-IP マッピングインターフェイスがある場合: OkHttp、libcurl、HarmonyOS Network Kit などのライブラリでは、HTTPDNS の結果を DNS インターフェイスに直接挿入できます。その後、ドメイン名を使用してリクエストを行うことができます。このメソッドは、追加の変更なしで HTTPS、サーバ名表示 (SNI)、Cookie、および接続の再利用と互換性があります。
DNS Hook はないが、API リクエスト制御がある場合: ネットワークライブラリが DNS の置き換えを許可しないが、URL、リクエストヘッダー、証明書ポリシー、リダイレクトなどの詳細の書き換えを許可する場合は、直接 IP 接続を使用できます。このプロセスには、HTTPDNS から IP アドレスを取得し、IP アドレスで URL を書き換え、Host、証明書、プロキシ、および Cookie に必要な処理を追加することが含まれます。
完全に閉じている場合: 一部の WebView カーネルなど、ネットワークライブラリに DNS Hook がなく、API 制御のエントリポイントもない場合、直接 IP 接続は実現不可能です。代わりに、SDK、DNS over HTTPS (DoH)、ローカルプロキシ、またはベンダー固有の機能の使用を検討してください。
まとめると、「セミオープン」なネットワークライブラリで DNS Hook をサポートしていない場合は、直接 IP 接続を使用して HTTPDNS と統合できます。
H5 と HTTPDNS の統合方法: JavaScript がブラウザで実行されるとき、Fetch Standard に従います。この標準では、Host は 禁止ヘッダー名 であると規定されています。したがって、XMLHttpRequest や fetch() などの API を介して行われるリクエストの場合、スクリプトは Host を変更できません。これは、直接 IP 接続を使用して HTTPDNS と統合できないことを意味します。これらのシナリオでは、WebView、CEF、Electron などのランタイム環境に依存して、ホストレイヤーでこの処理を実行する必要があります。または、ローカルプロキシまたは DoH ソリューションを使用します。
3. 問題と解決策
このセクションでは、まずクライアントとサーバーの相互作用に関連するネットワークの概念を紹介します。次に、直接 IP 接続の使用によって生じる問題とその解決策について説明します。
3.1 ドメイン名のセマンティクスに関連する概念
サーバ名表示 (SNI): SNI は、Transport Layer Security (TLS) プロトコルの拡張機能です。クライアントはハンドシェイク中にターゲットのドメイン名をサーバーに伝えます。これにより、サーバーは正しい証明書と仮想ホストを選択できます。SNI は、共有 IP またはマルチテナンシーのシナリオで特に重要です。たとえば、CDN ノードが複数のドメイン名を提供する場合、SNI は正しい HTTPS 証明書が返されることを保証します。これにより、接続の正当性とセキュリティが確認されます。
CN/SAN 証明書検証: TLS ハンドシェイク中、クライアントはサーバーから返された HTTPS 証明書を厳密に検証します。ネットワークライブラリは通常、URL からターゲットのドメイン名を抽出します。このドメイン名を、期待されるコモンネーム (CN) またはサブジェクト代替名 (SAN) として使用します。次に、証明書から実際の CN/SAN を解析して比較します。一致しない場合、接続は拒否されるか、信頼できない安全でない接続と見なされます。
Host ヘッダー: HTTP/1.1 以降、Host ヘッダーは必須フィールドです。同じ IP アドレスとポートで複数のサイトがホストされている場合に、ターゲットサイトを区別するために使用されます。サーバーにとっては、仮想ホストまたはリバースプロキシがリクエストを正確にルーティングするための主要な基盤です。中間レイヤーでは、キャッシュキー、認証ポリシー、およびレート制限ルールが Host レベルで定義されることがよくあります。クライアント側の Cookie 管理も、ドメインの分離のために Host に依存しています。Host が正しく構成されていない場合、ログイン状態が IP ドメインに書き込まれたり、再利用に失敗したりする可能性があります。
3.2 直接 IP 接続によって生じる問題
次の図に示すように、クライアントの元のリクエスト URL は https://www.example.com です。ネットワークライブラリが DNS Hook を提供していないため、HTTPDNS からの DNS 解決結果を適用するには、URL を https://1.2.3.4 に書き換える必要があります。URL が直接 IP 接続用に書き換えられると、クライアントとターゲットサービス間の相互作用中に次の問題が発生します。

サーバーが異常な HTTPS 証明書を返す: ネットワークライブラリが URL から抽出した SNI 値が
www.example.comから1.2.3.4に変更されると、ターゲットサーバーは一致する証明書を見つけることができません。デフォルトの HTTPS 証明書しか返すことができず、ハンドシェイクが失敗します。クライアントが HTTPS 証明書検証に失敗する: ネットワークライブラリは、期待される CN/SAN を
www.example.comではなく1.2.3.4として解析します。証明書内の CN/SAN (たとえばwww.example.comや*.example.com) が IP アドレスと一致しないため、TLS 検証が失敗し、ハンドシェイクを完了できません。サーバーがターゲットサービスにルーティングできない:
Nginx、Apache、その他のWeb サーバーなどの Web サーバーソフトウェアは、仮想ホストをサポートしています。この機能により、同じ物理サーバーとポートに異なるサイトを展開できます。リクエストはHostヘッダーに基づいて正しいサイトにルーティングされます。クライアントが URL から期待される Host を抽出すると、www.example.comから1.2.3.4に変更されます。これにより、サーバーは対応するサービスを見つけることができず、404状態コードを返します。クライアント側の Cookie 管理が失敗する: クライアントのネットワークライブラリは通常、
CookiesをHostヘッダーに基づいてキャッシュします。Cookieをドメイン名www.example.comではなく、特定の IP アドレス1.2.3.4にバインドします。これにより、元のドメイン下のCookiesの再利用が妨げられます。同じドメイン名の解決結果は、キャッシュの有効期限が切れた後、しばしば別の IP アドレスに切り替わるため、Cookiesはそれぞれの IP アドレスにしかバインドできません。これにより、ログイン状態が失われ、セッションを共有できなくなります。また、セキュリティポリシーが失敗する原因にもなります。クライアント接続を再利用できない: 最新のネットワークライブラリは、TCP ハンドシェイクのオーバーヘッドを削減し、応答効率を向上させるために、一般的に Host に基づく HTTP 接続の再利用をサポートしています。直接 IP 接続に切り替えると、再利用の粒度がドメイン名
www.example.comから IP アドレス1.2.3.4に変更されます。既存の接続は異なる IP アドレス間で共有できず、追加のハンドシェイクオーバーヘッドが発生します。
3.3 直接 IP 接続の問題に対する解決策
前述の問題は、直接 IP 接続が HTTPDNS によって解決された IP アドレスを使用するために発生します。これらの問題を解決するには、SNI、証明書検証、および HTTP Host ヘッダーのドメイン名情報を手動で復元する必要があります。これにより、ネットワークスタックがドメイン名のセマンティクスに基づいて動作し続けることが保証されます。
SNI を手動で設定する: ネットワークライブラリによって公開されている TLS/SNI 構成または SocketFactory API を呼び出して、元のドメイン名を SNI 拡張機能に書き込みます。
検証のために CN/SAN を手動で設定する:
HostnameVerifierなどのネットワークライブラリが提供する証明書検証 API を使用します。TLS 検証フェーズで元のドメイン名を明示的に渡し、CN/SAN の比較が正しいことを確認します。Host ヘッダーを手動で設定する: ネットワークライブラリが提供する API を使用して、
Host: [元のドメイン名]を明示的に設定します。これにより、IP アドレスがデフォルトで Host ヘッダーに書き込まれるのを防ぎます。説明HTTP プロキシは HTTP/1.1 の absolute-form に従います (詳細については、「origin-form」をご参照ください)。リクエストラインには完全な URL が含まれます。URL が IP アドレスに書き換えられると、プロキシはこの IP を実際のホストとして扱うことがよくあります。オリジンサーバーにリクエストを送信する際に、クライアントによって設定されたドメイン名の
Hostヘッダーを破棄することさえあります。これにより、オリジンサーバーが 404 エラーや証明書エラーを返したり、リクエストを拒否したりする可能性があります。ほとんどの場合、プロキシ環境では HTTPDNS を無効にしてください。
ネットワークライブラリによって、URL から SNI、証明書検証ドメイン、および Host ヘッダーを抽出する方法は異なります。このドメイン情報を復元するための API も異なります。特定のネットワークライブラリに基づいて、直接 IP 接続のソリューションを決定する必要があります。以下は、HTTPDNS が提供する直接 IP 接続の適応ソリューションです。
Android
HttpURLConnection:HostnameVerifier/SSLSocketFactoryを使用して TLS 検証ロジックを上書きし、setRequestPropertyメソッドを使用してHostヘッダーを明示的に設定します。詳細については、「Android で HttpURLConnection と HTTPDNS を使用するためのベストプラクティス」をご参照ください。iOS
NSURLSession:URLSession:task:didReceiveChallenge:で、証明書検証のためにドメイン名をアタッチし、リクエストを構築する際にHostヘッダーを設定します。詳細については、「iOS のネイティブシナリオで HTTPDNS を使用する」をご参照ください。Unity
HttpWebRequest:ServicePointManager.ServerCertificateValidationCallbackを使用してドメイン名検証を指定し、request.Host = original domain nameを使用して Host ヘッダーを書き換えます。詳細については、「Unity フレームワークのベストプラクティス」をご参照ください。
その他のネットワークスタックまたは IP 直接接続ソリューションについては、テクニカルサポートにお問い合わせください。
4. まとめ
直接 IP 接続は、DNS をカスタマイズできないシナリオでの救済策です。重要なのは、元のドメイン名を記録することです。また、証明書検証、SNI、および Host ヘッダーの 3 つの主要な領域でドメイン名のセマンティクスが使用されるようにする必要があります。これらの原則に従うことで、セミオープンなネットワークライブラリでも HTTPDNS からの解決結果を安全に使用できます。