このドキュメントでは、iOS クライアントで DoH を使用する方法について説明します。
背景
iOS に HTTPDNS を統合する一般的な方法は、HTTPDNS ソフトウェア開発キット (SDK) をインポートすることです。これにより、HTTPS 証明書の検証やサーバ名表示 (SNI) 拡張などの問題に対処できます。これにより、必要に応じてアプリで HTTPDNS の名前解決機能を使用できます。詳細については、「ネイティブ iOS シナリオで HTTPDNS を使用する」をご参照ください。
Apple は iOS 14 以降でセキュア DNS を導入し、DoH を介して HTTPDNS を統合するための新しい実現可能なソリューションを追加しました。次の 2 つの WWDC ビデオを参考にしてください。
アプリとサーバーの DNS セキュリティを向上させる: セキュア DNS の背景知識を紹介します。
暗号化 DNS を有効にする: システムレベルとアプリレベルの実装方法の両方を含む、セキュア DNS の使用方法を紹介します。
セキュア DNS の使用は iOS でネイティブにサポートされており、コードレベルでネットワークリクエストの詳細を変更することなく、アプリまたはデバイスのグローバルネットワーク処理レベルで直接有効になるため、ソリューション全体がより洗練されます。ただし、重大な制限もあります。セキュア DNS を介して HTTPDNS が統合されている場合:
アプリレベルまたはデバイスレベルでのみ有効になり、対応する範囲内のすべてのドメイン名解決は、アプリ内のサードパーティ SDK からのネットワークリクエストを含め、HTTPDNS を経由します。きめ細かいレベルで制御することはできません。
デバイスレベルで有効にすることを選択した場合、エンドユーザーは特別なアクセス許可を付与する必要があります。通常、ネットワークユーティリティアプリケーションのみがこのアクセス許可を申請できます。
前提条件
DoH を有効にし、DoH インジェストエンドポイントを取得します。詳細については、「DoH サービスを構成する」をご参照ください。
DoH が有効になっていない場合、解決リクエストは失敗し、HTTPDNS サーバーは 400 エラーコードを返します。
「ドメイン解決範囲」を「ドメインリスト内のドメイン」に設定した場合、リストにないドメインに対して、HTTPDNS サーバーは 200 ステータスコードを返しますが、解決結果は返しません。
「ドメイン解決の範囲」が「すべてのドメイン」に設定されている場合、ブラックリスト内のドメインの場合、HTTPDNS サーバーは 200 状態コードを返しますが、解決結果は返しません。
アプリケーションレベルの DoH 構成の使用
iOS 14 以降の network.framework は、privacyContext を提供して、アプリケーションに対して DoH を個別に構成し、アプリケーションのライフサイクル中に DNS 解決プロセスを制御できるようにします。
サンプルコード
NSURLSession を管理するために DataTaskManager を作成します。
@interface DataTaskManager : NSObject <NSURLSessionTaskDelegate>#import "DataTaskManager.h"
@import Network;
@import Foundation;
- (instancetype)init {
self = [super init];
if (self) {
_networkQueue = dispatch_queue_create("com.taskmanager.queue", DISPATCH_QUEUE_SERIAL);
[self setupDoHConfiguration];
}
return self;
}
- (void)setupDoHConfiguration {
dispatch_async(self.networkQueue, ^{
NSLog(@"DoH 構成を設定しています...");
// HTTPDNS DoH の URL エンドポイントを作成します
const char *dohServerURL = "https://1xxxx3.aliyunhttpdns.com/dns-query";
nw_endpoint_t urlEndpoint = nw_endpoint_create_url(dohServerURL);
NSLog(@"DoH サーバーを使用しています: %s", dohServerURL);
nw_resolver_config_t resolverConfig = nw_resolver_config_create_https(urlEndpoint);
nw_privacy_context_require_encrypted_name_resolution(NW_DEFAULT_PRIVACY_CONTEXT, true, resolverConfig);
NSLog(@"プライバシーコンテキストに DoH 構成が適用されました");
});
}ViewController.viewDidLoad で初期化を完了します。
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
......
self.dataTaskManager = [[DataTaskManager alloc] init];
......
}URLSession と URLSession に基づくサードパーティのネットワークライブラリは、ネットワークリクエストを行う際にデフォルトの PrivacyContext インスタンスを使用します。DoH 構成が完了すると、現在のアプリのすべての URLSession リクエストは DNS 解決に DoH を使用します。
解決パフォーマンスのイベントトラッキング
NSURLSessionTaskMetrics を使用して DNS プロセスを計測できます。これにより、DoH 解決が機能しているかどうかを確認し、DNS 解決時間を監視できます。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
NSLog(@"\n=== %@ へのリクエストのメトリックを収集しています ===\n", task.originalRequest.URL);
task.taskDescription = [NSString stringWithFormat:@"%.2f,%@",
metrics.taskInterval.duration,
task.originalRequest.URL.absoluteString];
if ([metrics.transactionMetrics count] > 0) {
[metrics.transactionMetrics enumerateObjectsUsingBlock:^(NSURLSessionTaskTransactionMetrics *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
NSString *fetchTypeStr = @"不明";
switch (obj.resourceFetchType) {
case NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad:
fetchTypeStr = @"ネットワークロード";
break;
case NSURLSessionTaskMetricsResourceFetchTypeServerPush:
fetchTypeStr = @"サーバープッシュ";
break;
case NSURLSessionTaskMetricsResourceFetchTypeLocalCache:
fetchTypeStr = @"ローカルキャッシュ";
break;
}
NSLog(@"フェッチタイプ: %@", fetchTypeStr);
if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol;
NSString *dnsProtocolStr = @"不明 (0)";
BOOL isDoH = NO;
switch (dnsProtocol) {
case NSURLSessionTaskMetricsDomainResolutionProtocolUDP:
dnsProtocolStr = @"UDP (1)";
break;
case NSURLSessionTaskMetricsDomainResolutionProtocolTCP:
dnsProtocolStr = @"TCP (2)";
break;
case NSURLSessionTaskMetricsDomainResolutionProtocolTLS:
dnsProtocolStr = @"TLS (3)";
break;
case NSURLSessionTaskMetricsDomainResolutionProtocolHTTPS:
dnsProtocolStr = @"HTTPS/DoH (4)";
isDoH = YES;
break;
}
NSLog(@"DNS プロトコル: %@", dnsProtocolStr);
#if TARGET_OS_SIMULATOR
NSLog(@"シミュレータで実行中 - DNS プロトコル検出はサポートされていません");
#else
if (!isDoH) {
NSLog(@"DoH が検出されませんでした");
}
#endif
// DNS 解決パフォーマンスデータを取得します
if (obj.domainLookupStartDate && obj.domainLookupEndDate) {
int dnsLookupTime = ceil([obj.domainLookupEndDate timeIntervalSinceDate:obj.domainLookupStartDate] * 1000);
NSLog(@"DNS ルックアップの詳細:");
NSLog(@" 開始: %@", obj.domainLookupStartDate);
NSLog(@" 終了: %@", obj.domainLookupEndDate);
NSLog(@" 期間: %d ミリ秒", dnsLookupTime);
} else {
NSLog(@"DNS ルックアップは実行されませんでした (キャッシュされている可能性があります)");
}
// ネットワークリクエストパフォーマンスデータを取得します
if (obj.connectStartDate && obj.connectEndDate) {
NSTimeInterval connectionTime = [obj.connectEndDate timeIntervalSinceDate:obj.connectStartDate];
NSLog(@"接続時間: %.3f 秒", connectionTime);
}
}
}];
} else {
NSLog(@"トランザクションメトリックは利用できません");
}
}フォールバックメカニズム
フォールバックの完了後、アプリケーション内の後続のすべての URLSession ベースのリクエストに影響します。
DoH 構成は、実際の iOS 14 以降のデバイスおよびエミュレーターで動作します。ただし、エミュレーターでアプリを実行すると、
NSURLSessionTaskMetricsによって読み取られるNSURLSessionTaskMetricsDomainResolutionProtocolは常に 0 (不明) になります。エミュレーターで関連機能をテストする際は、この違いに注意してください。
DNS 解決が失敗した場合に例外情報を迅速に取得するために、タイムアウト設定を構成します。
// 接続タイムアウトを設定します
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.waitsForConnectivity = NO;
config.timeoutIntervalForRequest = 5;
config.timeoutIntervalForResource = 10;上記の計測チェーンでは、適切なフォールバック戦略を策定できます。DNS 例外が検出されたときにグローバルに LocalDNS にフォールバックする例を次に示します。
......
if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol;
NSString *dnsProtocolStr = @"不明 (0)";
BOOL isDoH = NO;
switch (dnsProtocol) {
case NSURLSessionTaskMetricsDomainResolutionProtocolUDP:
dnsProtocolStr = @"UDP (1)";
break;
case NSURLSessionTaskMetricsDomainResolutionProtocolTCP:
dnsProtocolStr = @"TCP (2)";
break;
case NSURLSessionTaskMetricsDomainResolutionProtocolTLS:
dnsProtocolStr = @"TLS (3)";
break;
case NSURLSessionTaskMetricsDomainResolutionProtocolHTTPS:
dnsProtocolStr = @"HTTPS/DoH (4)";
isDoH = YES;
break;
}
NSLog(@"DNS プロトコル: %@", dnsProtocolStr);
#if TARGET_OS_SIMULATOR
NSLog(@"シミュレータで実行中 - DNS プロトコル検出はサポートされていません");
#else
if (!isDoH) {
NSLog(@"DoH が検出されませんでした。ローカル DNS にフォールバックしています");
dispatch_async(dispatch_get_main_queue(), ^{
// DoH を無効にして LocalDNS を使用します
nw_privacy_context_require_encrypted_name_resolution(NW_DEFAULT_PRIVACY_CONTEXT, false, nil);
});
}
#endif
}
......「ドメイン解決の範囲」が「ドメインリスト内のドメイン」に設定されている場合。
ドメインリストにないドメインの場合、この例の dnsProtocolStr は "Unknown (0)" と出力します。
ドメインリスト内のドメインの場合、この例の dnsProtocolStr は "HTTPS/DoH (4)" と出力します。
「ドメイン解決の範囲」が「すべてのドメイン」に設定されている場合。
ブラックリスト内のドメインの場合、例の dnsProtocolStr は「不明 (0)」と出力されます。
ブラックリストにないドメインの場合、例の dnsProtocolStr は「HTTPS/DoH (4)」と出力されます。
システムレベルの DoH 構成の使用
システムプロファイルを構成することで、iOS デバイスの DoH を構成できます。システムレベルの DoH はデバイス上のすべてのアプリケーションに影響することに注意してください。
DoH アドレスを構成するには、次の手順に従います。
サンプルコンテンツ内の DoH アドレスを置き換え、コンテンツを
.mobileconfigファイル (例:my_company_doh.mobileconfig) として保存します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>DNSSettings</key>
<dict>
<key>DNSProtocol</key>
<string>HTTPS</string>
<key>ServerURL</key>
<!---- ここでアドレスを DoH アクセスアドレスに置き換えます --->
<string>https://1xxxx3.aliyunhttpdns.com/dns-query</string>
</dict>
<key>PayloadDescription</key>
<string>EMAS HTTPDNS DoH を使用するように iOS を構成します</string>
<key>PayloadDisplayName</key>
<string>EMAS HTTPDNS DoH</string>
<key>PayloadIdentifier</key>
<string>com.apple.dnsSettings.managed.9B498EC0C-EF6C-44F0-BFB7-0000658B99AC</string>
<key>PayloadType</key>
<string>com.apple.dnsSettings.managed</string>
<key>PayloadUUID</key>
<string>465AB183-5E34-4794-9BEB-B5327CF61F27</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>ProhibitDisablement</key>
<false/>
</dict>
</array>
<key>PayloadDescription</key>
<string>iOS に EMAS HTTPDNS DoH 構成を追加します</string>
<key>PayloadDisplayName</key>
<string>EMAS HTTPDNS DoH 構成</string>
<key>PayloadIdentifier</key>
<string>com.emas.apple-dns</string>
<key>PayloadRemovalDisallowed</key>
<false/>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>130E6D6F-69A2-4515-9D77-99342CB9AE76</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>
my_company_doh.mobileconfigをファイルストレージサーバーに公開するか、電子メールで iOS デバイスに送信します。iOS デバイスで、ブラウザまたは電子メールクライアントを使用して
my_company_doh.mobileconfigをダウンロードします。設定全般VPN およびデバイス管理 > > でこのプロファイルをインストールします。