このドキュメントでは、HTTPDNS を iOS 14 のネイティブ暗号化 DNS ソリューションと統合する方法について説明します。
概要
DNS 解決は、アプリが行う最初のネットワークホップです。従来のローカル DNS は、暗号化されていない UDP を介してクエリを送信するため、ハイジャックや傍受の危険にさらされます。iOS 14 は、2 つの標準的な暗号化 DNS プロトコルをネイティブにサポートしています:
DNS over HTTPS (DoH):クエリをポート 443 の標準 HTTPS トラフィック内でルーティングし、Web リクエストと区別できないようにすることで、ブロックや監視を難しくします。
DNS over TLS (DoT):専用ポート (853) で TLS を使用してクエリを暗号化します。これは実装が高速ですが、ネットワークの中間者が識別してフィルタリングすることが容易です。
ほとんどのアプリにとって、DoH はよりシンプルな選択肢です。特別なファイアウォールルールを必要とせず、既存の HTTPS トラフィックに溶け込みます。
HTTPDNS は SDK ベースのソリューションを提供しますが、統合のオーバーヘッドが伴います。302 リダイレクトシナリオにおける直接 IP 接続の処理や、iOS での Server Name Indication (SNI) 問題の回避策などが必要になるためです。iOS 14 のネイティブ暗号化 DNS 機能は、HTTPDNS をリゾルバーとしてシステムに暗号化された名前解決をネイティブに処理させることで、これらの懸念を解消します。
iOS 14 のネイティブ暗号化 DNS ソリューションは、iOS 14 以降を搭載した物理デバイスでのみサポートされています。
DoH または DoT サービスが利用できない場合に備えて、名前解決の失敗を防ぐためにローカル DNS へのフォールバックメカニズムを実装してください。
SDK 統合ソリューションとは異なり、ネイティブソリューションは自動フェイルオーバーをサポートせず、サービスレベルアグリーメント (SLA) も提供しません。本番環境でネイティブソリューションを採用する場合は、サービス例外の監視と、デバイスのローカル DNS への自動フェイルオーバーを実装する必要があります。
HTTPDNS と iOS 14 のネイティブ暗号化 DNS ソリューションの統合
iOS 14 は、2 つのスコープの暗号化 DNS 設定をサポートしています。ユースケースに合わせてスコープを選択してください:
方法 | スコープ | API | 言語 |
アプリレベル | 単一アプリ内のすべての接続に対する暗号化 DNS |
| Objective-C |
システムワイド | デバイス上のすべてのアプリに対する暗号化 DNS |
| Swift |
単一アプリでの暗号化 DNS の有効化
デバイス上の他のアプリに影響を与えずに、自アプリ内でのみ暗号化 DNS を使用するには、NetworkExtension の nw_privacy_context_t を使用します。アプリ内で開始されるすべての DNS 解決で、この設定が使用されます。
参照用のデモが利用可能です: 単一アプリのすべての接続に対して DoH を有効にするデモ。
アプリレベルのすべての例では、nw_privacy_context_require_encrypted_name_resolution を NW_DEFAULT_PRIVACY_CONTEXT で使用して、暗号化解決を有効化します。HTTPDNS サーバーアドレス、プロトコル、およびフォールバックサーバーは、DoH と DoT で唯一異なるパラメーターです。
アプリレベルの DoH (Objective-C)
#import <NetworkExtension/NetworkExtension.h>
if (@available(iOS 14.0, *)){
nw_privacy_context_t defaultPrivacyContext = NW_DEFAULT_PRIVACY_CONTEXT;
nw_endpoint_t dohResolverEndpoint = nw_endpoint_create_url("https://*****-************.alidns.com/dns-query"); // DoH 暗号化アドレス
nw_endpoint_t v4ResolverEndpoint1 = nw_endpoint_create_host("223.5.5.5", "443");
nw_endpoint_t v4ResolverEndpoint2 = nw_endpoint_create_host("223.6.6.6", "443");
nw_endpoint_t v6ResolverEndpoint1 = nw_endpoint_create_host("2400:3200::1", "443");
nw_endpoint_t v6ResolverEndpoint2 = nw_endpoint_create_host("2400:3200:baba::1", "443");
nw_resolver_config_t fallbackResolvers = nw_resolver_config_create_https(dohResolverEndpoint);
nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint1);
nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint2);
nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint1);
nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint2);
nw_privacy_context_require_encrypted_name_resolution(defaultPrivacyContext, true, fallbackResolvers);
}アプリレベルの DoT (Objective-C)
#import <NetworkExtension/NetworkExtension.h>
if (@available(iOS 14.0, *)){
nw_privacy_context_t defaultPrivacyContext = NW_DEFAULT_PRIVACY_CONTEXT;
nw_endpoint_t dotResolverEndpoint = nw_endpoint_create_host("******-************.alidns.com", "853"); // DoT 暗号化アドレス
nw_endpoint_t v4ResolverEndpoint1 = nw_endpoint_create_host("223.5.5.5", "853");
nw_endpoint_t v4ResolverEndpoint2 = nw_endpoint_create_host("223.6.6.6", "853");
nw_endpoint_t v6ResolverEndpoint1 = nw_endpoint_create_host("2400:3200::1", "853");
nw_endpoint_t v6ResolverEndpoint2 = nw_endpoint_create_host("2400:3200:baba::1", "853");
nw_resolver_config_t fallbackResolvers = nw_resolver_config_create_tls(dotResolverEndpoint);
nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint1);
nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint2);
nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint1);
nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint2);
nw_privacy_context_require_encrypted_name_resolution(defaultPrivacyContext, true, fallbackResolvers);
}ローカル DNS へのフォールバック
ネイティブソリューションには自動フェイルオーバーがないため、名前解決の失敗を検出し、手動でローカル DNS に戻す必要があります。URLSessionDelegate を使用して、各リクエストの後に NSURLSessionTaskTransactionMetrics を読み取ります。domainResolutionProtocol が Unknown を返す場合、暗号化されたリゾルバーは到達不能です。直ちに無効にしてください。
プロトコルの値は、次のように対応します:
値 | プロトコル |
| Unknown |
| UDP |
| TCP |
| TLS |
| HTTPS |
#pragma mark URLSession Delegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
if ([metrics.transactionMetrics count] <= 0) return;
[metrics.transactionMetrics enumerateObjectsUsingBlock:^(NSURLSessionTaskTransactionMetrics *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
if (@available(iOS 14.0, *)) {
NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol;
NSLog(@"%@",[NSString stringWithFormat:@"DNS type is %ld", (long)dnsProtocol]);
NSLog(@"%@",[NSString stringWithFormat:@"0:Unknown,1:UDP,2:TCP,3:TLS,4:HTTPS"]);
if (dnsProtocol == NSURLSessionTaskMetricsDomainResolutionProtocolUnknown) {
NSLog(@"%@",[NSString stringWithFormat:@"DNS source unknown, disabling DoH"]);
nw_privacy_context_require_encrypted_name_resolution(NW_DEFAULT_PRIVACY_CONTEXT, false, nil);
}
}
}
}];
}システムワイドでの暗号化 DNS の有効化
デバイス上のすべてのアプリに暗号化された DNS を適用するには、NEDNSSettingsManager API を使用する NetworkExtension アプリを作成します。システムが DNS 設定を管理するため、Network Extension プロバイダーまたはプラグインを実装する必要はありません。Xcode プロジェクトのネットワーク機能拡張で、[DNS 設定] を選択します。
DNS 設定は、HTTPDNS サーバーアドレス、プロトコル (DoH または DoT)、および設定がいつ有効になるかを制御する一連の NEOnDemandRule ネットワークルールの 3 つの部分で構成されます。
システムワイドの DoH (Swift)
import NetworkExtension
NEDNSSettingsManager.shared().loadFromPreferences { loadError in
if let loadError = loadError {
// エラー処理
return
}
let dohSettings = NEDNSOverHTTPSSettings(servers: ["223.5.5.5","223.6.6.6","2400:3200:baba::1","2400:3200::1"])
dohSettings.serverURL = URL(string: "https://*****-************.alidns.com/dns-query") // DoH 暗号化アドレス
NEDNSSettingsManager.shared().dnsSettings = dohSettings
NEDNSSettingsManager.shared().saveToPreferences { saveError in
if let saveError = saveError {
// エラー処理
return
}
}
}
システムワイドの DoT (Swift)
import NetworkExtension
NEDNSSettingsManager.shared().loadFromPreferences { loadError in
if let loadError = loadError {
// エラー処理
return
}
let dotSettings = NEDNSOverTLSSettings(servers: ["223.5.5.5","223.6.6.6","2400:3200:baba::1","2400:3200::1"])
dotSettings.serverName = "******-************.alidns.com" // DoT 暗号化アドレス
NEDNSSettingsManager.shared().dnsSettings = dotSettings
NEDNSSettingsManager.shared().saveToPreferences { saveError in
if let saveError = saveError {
// エラー処理
return
}
}
}ネットワークルールの設定
ネットワークルール (NEOnDemandRule) は、DNS 設定をアクティブ化するネットワークおよび SSID を制御します。次の例では、3 つのルールを設定します。
Wi-Fi ネットワーク
MyWorkWiFiでは暗号化 DNS が有効ですが、プライベートドメインenterprise.exampleはローカルで解決されます (暗号化リゾルバーを経由することはありません)。携帯電話ネットワークでは、暗号化 DNS は無効になります。
他のすべてのネットワークでは、暗号化 DNS はデフォルトで有効になります。
let workWiFi = NEOnDemandRuleEvaluateConnection() workWiFi.interfaceTypeMatch = .wiFi workWiFi.ssidMatch = ["MyWorkWiFi"] workWiFi.connectionRules = [NEEvaluateConnectionRule(matchDomains: ["enterprise.example"], andAction: .neverConnect)] let disableOnCell = NEOnDemandRuleDisconnect() disableOnCell.interfaceTypeMatch = .cellular let enableByDefault = NEOnDemandRuleConnect() NEDNSSettingsManager.shared().onDemandRules = [ workWiFi, disableOnCell, enableByDefault ]
設定の有効化
NetworkExtension アプリを実行すると、DNS 設定はデバイスにインストールされますが、まだ有効になっていません。有効にするには、 [設定] > [一般] > [VPNとネットワーク] > [DNS] に移動し、設定をオンにします。