暗号化DNSの有効化 – WWDC2020

Session概要

アプリ内でWebにアクセスする場合は、そのプライバシーが非常に重要になります。Appleのプラットフォーム全体で暗号化DNSを活用することによってその情報を守り、アプリ内でプライベートかつ安全な接続性を提供しましょう。
システムのDNS設定を使用して暗号化サーバに接続する、または標準的なネットワークAPIを使用してアプリ内で暗号化DNSを有効化する方法について https://developer.apple.com/videos/play/wwdc2020/10047/

DNSとプライバシーの重要性

  • DNS(Domain Name System)とは
    • SafariなどのWebページのアドレスを IPアドレスに変換するシステム

DNSとプライバシーの関係性

  • 通常 DNSのリクエストとレスポンスは 未暗号化のUDPで送受信されるので、検索ページがネットワーク上の外部デバイスに知られる可能性がある
    • UDP(User Datagram Protocol)
      • 通信プロトコルの一つで、転送速度は高いが信頼性が低い
  • 偽装も可能で、ローカルネットワークのDNSリゾルバーの信頼性が低いことも問題で公共のWi-Fiにアクセスした場合、追跡やブロックされる可能性がある

DNSの暗号化

  • 暗号を使用してDNSのリクエストとレスポンス保護する
  • AppleはDNS暗号化のサポートを開始
  • 保護用のプロトコルは2つ
    • DoTと呼ばれるDNS over TLS
    • DoHと呼ばれるDNS over HTTPS
    • 両方ともTLSを使用し、DNSメッセージを暗号化する
    • DoHはHTTPを使用し、パフォーマンスを改善します
  • DNSの暗号化2つの方法
    1. デフォルトのリゾルバーとしてすべてのアプリに 1つのDNSサーバーを選択する
      • そうすることで、パブリックDNSサーバーの提供のため、システムを設定するNetworkExtensionアプリを作成可能
      • もし MDM(モバイルデバイス管理)を使ってデバイス上の エンタープライズ設定を行うのであれば、プロファイルをプッシュダウンしネットワークに対し暗号化を設定可能
    2. アプリから直接オプトインする
      • アプリに対してだけでも暗号化を望むなら、特定のサーバーを選択し選んだアプリの接続に使用可能

システム全体での設定
アプリ単位での設定

システム全体のDNS設定

  • 2つの設定方法
    1. NEDNSSettingsManagerを使用するNetworkExtensionアプリで設定
    2. DNSSettingsペイロードを含むMDMプロファイルで設定
  • どちらも同じ内容を指定可能で、
    • サーバーとプロトコルを特定するDNSサーバー設定
    • 設定のタイミングをカスタマイズするネットワークルールのセット
  • NetworkExtensionで サーバーコンフィグレーションを指定する
    • NEDNSSettingsManagerオブジェクトで 既存のコンフィグレーションでロードする
    • そうすれば、DoHかDoTサーバーのいずれかの設定を定義することができる

NetworkExtensionでのサーバーコンフィグレーション指定

// Create a DNS configuration

import NetworkExtension

// NEDNSSettingsManagerオブジェクトで 既存のコンフィグレーションでロード
NEDNSSettingsManager.shared().loadFromPreferences { loadError in
    if let loadError = loadError {
        // ...handle error...
        return
    }
    let dohSettings = NEDNSOverHTTPSSettings(servers: [ "2001:db8::2" ])
    dohSettings.serverURL = URL(string: "https://dnsserver.example.net/dns-query")
    NEDNSSettingsManager.shared().dnsSettings = dohSettings

    // これでユーザーはDNSサーバーを設定アプリ内で有効にできる
    NEDNSSettingsManager.shared().saveToPreferences { saveError in
        if let saveError = saveError {
            // ...handle error...
            return
        }
    }
}

コンフィグレーションの ネットワークルール

  • ネットワークルールの指定により、別のネットワークとの互換性を保証する
  • パブリックDNSサーバはローカルネットワーク上 だけのプライベートネームを解決しない
    • 例えば、エンタープライズWi-Fiネットワーク上のDNSサーバーのみが従業員がアクセスするプライベートネームを解決する
  • 互換性の中には、自動的に処理されるものもあり、ネットワークルールを指定する必要はな
    • カフェなどのキャプティブネットワーク(ユーザ登録や通信料の支払いが必要な公共の Wi-Fi ネットワークのこと)は自動的に例外として検出される
    • 同様にVPNがアクティブだとVPNトンネル内での解決にVPNのDNS設定が使用され、システム全体の設定ではありません
  • ただし、エンタープライズWi-Fiネットワーク上の プライベートネームを処理するにはネットワークルールの設定が必要
    • ネットワークルールは Wi-Fiやセルラー特定のWi-Fi SSIDのような ネットワークタイプの挙動を定義する
  • 適合済みのネットワークでは、DNS設定を完全に無効にするか、特定のプライベートドメインに対してのみ例外を与えられる
    • すなわち、アプリ内でネットワークルールを設定する方法でユーザーはこれらのルール設定が可能となる

// Apply network rules

// 社内ネットワークの例外作成のため、特定SSIDでWi-Fiのルールを定義
let workWiFi = NEOnDemandRuleEvaluateConnection()
workWiFi.interfaceTypeMatch = .wiFi
workWiFi.ssidMatch = ["MyWorkWiFi"]
workWiFi.connectionRules =
    [ NEEvaluateConnectionRule(matchDomains: ["enterprise.example.net"],
                               andAction: .neverConnect) ]

// Disconnectルールでネットワークのカテゴリー全体で設定を無効
let disableOnCell = NEOnDemandRuleDisconnect()
disableOnCell.interfaceTypeMatch = .cellular

let enableByDefault = NEOnDemandRuleConnect()

NEDNSSettingsManager.shared().onDemandRules = [
    workWiFi,
    disableOnCell,
    enableByDefault
]

XcodeでのDNS設定機能

  • プロジェクト設定のSigning and Capabilitiesをクリック
  • DNS設定機能は NetworkExtension機能の1つであるため、システムがサポートするプロトコルを設定する

DNS Blocking

  • ネットワークによっては、ポリシーで暗号化サーバーをブロックするものもあり、クエリを見て トラフィックをフィルタリングする可能性がある
  • これが発生するとWi-Fiネットワークにプライバシー警告が付く
    • プライバシー侵害の前にアプリの接続が失敗となる

アプリ単位でのDNS暗号化

  • アプリからオプトインする場合、Networkフレームワークで PrivacyContexts を選ぶ

// Use encrypted DNS with NWConnection

import Network

let privacyContext = NWParameters.PrivacyContext(description: "EncryptedDNS")
if let url = URL(string: "https://dnsserver.example.net/dns-query") {
    let address = NWEndpoint.hostPort(host: "2001:db8::2", port: 443)
    privacyContext.requireEncryptedNameResolution(true,
        fallbackResolver: .https(url, serverAddresses: [ address ]))
}

let tlsParams = NWParameters.tls
tlsParams.setPrivacyContext(privacyContext)

let conn = NWConnection(host: "www.example.com", port: 443, using: tlsParams)
conn.start(queue: .main)
// Validate which DNS protocol was used

import Network

conn.requestEstablishmentReport(queue: .main) { report in
    if let report = report {
        for resolution in report.resolutions {
            // 使用したプロトコルの確認
            switch resolution.dnsProtocol {
            case .https, .tls:
                print("Used encrypted DNS!”)
            case .udp, .tcp:
                print("Used unencrypted DNS")
            default:
                // Ignore unknown protocols
       }
    }
}
// Use encrypted DNS for other APIs

import Network

if let url = URL(string: "https://dnsserver.example.net/dns-query") {
    let address = NWEndpoint.hostPort(host: "2001:db8::2", port: 443)
    NWParameters.PrivacyContext.default.requireEncryptedNameResolution(true,
        fallbackResolver: .https(url, serverAddresses: [ address ]))
}

let task = URLSession.shared.dataTask(with: ...)
task.resume()

getaddrinfo(...)
最新情報をチェックしよう!