DNS 的安全傳輸

傳統 DNS 查詢和回覆會透過 UDP 或 TCP 傳送,且未經過加密,因此會經過監控、假冒和 DNS 式網際網路篩選。由於訊息可能會通過許多網路,而遞迴解析器與權威名稱伺服器之間的訊息通常會整合額外的保護措施,因此對 Google 公用 DNS 等公開解析器發出的用戶端回應而言特別容易遭受攻擊。

為解決這些問題,我們曾在 2016 年透過 HTTPS 推出 DNS (現稱 DoH),提供透過 HTTPS 和 QUIC 加密 DNSSEC 驗證的 DNS 解析服務。2019 年,我們已新增支援 Android 私人 DNS 功能使用的 DNS over TLS (DoT) 標準。

DoH 和 DoT 可強化用戶端和解析器之間的隱私與安全性,並輔助 DNSSEC 的 Google 公用 DNS 驗證,為 DNSSEC 簽署的網域提供端對端驗證 DNS。使用 Google 公用 DNS 時,我們致力為 DoH 和 DoT 用戶端提供快速、私密且安全的 DNS 解析服務。

支援的 TLS 版本和加密套件

Google 公用 DNS 適用於 DoH 和 DoT 的 TLS 1.2 和 TLS 1.3,不支援較舊的 TLS 或 SSL。僅支援具備正向安全性和額外資料的驗證加密 (AEAD) 加密套件。Qualys SSL Labs 會顯示目前支援的加密套件組合。

端點

Google 公用 DNS 採用 DoH 和 DoT 使用的端點如下:

DoT (通訊埠 853) dns.google

DoH (通訊埠 443) URI 範本

  • RFC 8484 - https://dns.google/dns-query{?dns}

    • 對於 POST 來說,網址只是 https://dns.google/dns-query,而 HTTP 要求的主體是包含 app/dns-message 的二進位檔 UDP DNS 酬載。
    • 對於 GET,這是指 https://dns.google/dns-query?dns=BASE64URL_OF_QUERY
  • JSON API - https://dns.google/resolve{?name}{&type,cd,do,…}

    • 如要進一步瞭解 GET 參數,請參閱 JSON API 頁面。只需要 name 參數。

客戶

許多用戶端應用程式都使用 DoT 或 DoH

  • Android 9 (Pie)「私密瀏覽」功能 – DoT
  • Intra (Android 應用程式) – DoH

dnsprivacy.org 網站會列出其他幾個適用於 DoT 和 DoH 的用戶端,但這些用戶端通常要進行中等的技術設定。

指令列範例

以下指令列範例不適合用於實際用戶端,僅供參考,其中僅用一般可用的診斷工具。

DoT

下列指令需要 Knot DNS kdig 2.3.0 以上版本;如果是 2.7.4 以上版本,請取消註解 +tls‑sni,按照 TLS 1.3 的要求傳送 SNI

kdig -d +noall +answer @dns.google example.com \
  +tls-ca +tls-hostname=dns.google # +tls-sni=dns.google
;; DEBUG: Querying for owner(example.com.), class(1), type(1), server(dns.google), port(853), protocol(TCP)
;; DEBUG: TLS, imported 312 system certificates
;; DEBUG: TLS, received certificate hierarchy:
;; DEBUG:  #1, C=US,ST=California,L=Mountain View,O=Google LLC,CN=dns.google
;; DEBUG:      SHA-256 PIN: lQXSLnWzUdueQ4+YCezIcLa8L6RPr8Wgeqtxmw1ti+M=
;; DEBUG:  #2, C=US,O=Google Trust Services,CN=Google Internet Authority G3
;; DEBUG:      SHA-256 PIN: f8NnEFZxQ4ExFOhSN7EiFWtiudZQVD2oY60uauV/n78=
;; DEBUG: TLS, skipping certificate PIN check
;; DEBUG: TLS, The certificate is trusted.

;; ANSWER SECTION:
example.com.            2046    IN      A       93.184.216.34
kdig -d +noall +answer @dns.google example.com \
  +tls-pin=f8NnEFZxQ4ExFOhSN7EiFWtiudZQVD2oY60uauV/n78= \
  # +tls-sni=dns.google
;; DEBUG: Querying for owner(example.com.), class(1), type(1), server(dns.google), port(853), protocol(TCP)
;; DEBUG: TLS, received certificate hierarchy:
;; DEBUG:  #1, C=US,ST=California,L=Mountain View,O=Google LLC,CN=dns.google
;; DEBUG:      SHA-256 PIN: lQXSLnWzUdueQ4+YCezIcLa8L6RPr8Wgeqtxmw1ti+M=
;; DEBUG:  #2, C=US,O=Google Trust Services,CN=Google Internet Authority G3
;; DEBUG:      SHA-256 PIN: f8NnEFZxQ4ExFOhSN7EiFWtiudZQVD2oY60uauV/n78=, MATCH
;; DEBUG: TLS, skipping certificate verification

;; ANSWER SECTION:
example.com.            5494    IN      A       93.184.216.34

DoH

RFC 8484 POST

這個指令中的 Base64Url 編碼字串是 dig +noedns example.test A 傳送的 DNS 訊息,其 DNS ID 欄位已設為零,如 RFC 8484 第 4.1 節的建議。殼層指令會使用 Content-Type application/dns-message,將 DNS 查詢做為二進位檔資料主體內容傳送。

echo AAABAAABAAAAAAAAB2V4YW1wbGUEdGVzdAAAAQAB | base64 --decode |
 curl -is --data-binary @- -H 'content-type: application/dns-message' \
   https://dns.google/dns-query
HTTP/2 200
strict-transport-security: max-age=31536000; includeSubDomains; preload
access-control-allow-origin: *
date: Wed, 29 May 2019 19:37:16 GMT
expires: Wed, 29 May 2019 19:37:16 GMT
cache-control: private, max-age=19174
content-type: application/dns-message
server: HTTP server (unknown)
content-length: 45
x-xss-protection: 0
x-frame-options: SAMEORIGIN
alt-svc: quic=":443"; ma=2592000; v="46,44,43,39"

RFC 8484 GET

這個指令中的 Base64Url 編碼字串是 dig +noedns example.com A 傳送的 DNS 訊息,並將 DNS ID 欄位設為零。在這種情況下,會明確在網址中傳遞。

curl -i https://dns.google/dns-query?dns=AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAAABAAE
HTTP/2 200
strict-transport-security: max-age=31536000; includeSubDomains; preload
access-control-allow-origin: *
date: Wed, 29 May 2019 19:37:16 GMT
expires: Wed, 29 May 2019 19:37:16 GMT
cache-control: private, max-age=19174
content-type: application/dns-message
server: HTTP server (unknown)
content-length: 45
x-xss-protection: 0
x-frame-options: SAMEORIGIN
alt-svc: quic=":443"; ma=2592000; v="46,44,43,39"

JSON GET

這會使用 DoH 的 JSON API。

curl -i 'https://dns.google/resolve?name=example.com&type=a&do=1'
HTTP/2 200
strict-transport-security: max-age=31536000; includeSubDomains; preload
access-control-allow-origin: *
date: Thu, 30 May 2019 02:46:46 GMT
expires: Thu, 30 May 2019 02:46:46 GMT
cache-control: private, max-age=10443
content-type: application/x-javascript; charset=UTF-8
server: HTTP server (unknown)
x-xss-protection: 0
x-frame-options: SAMEORIGIN
alt-svc: quic=":443"; ma=2592000; v="46,44,43,39"
accept-ranges: none
vary: Accept-Encoding

{"Status": 0,"TC": false,"RD": true,"RA": true,"AD": true,"CD": false,"Question":[ {"name": "example.com.","type": 1}],"Answer":[ {"name": "example.com.","type": 1,"TTL": 10443,"data": "93.184.216.34"},{"name": "example.com.","type": 46,"TTL": 10443,"data": "a 8 2 86400 1559899303 1558087103 23689 example.com. IfelQcO5NqQIX7ZNKI245KLfdRCKBaj2gKhZkJawtJbo/do+A0aUvoDM5A7EZKcF/j8SdtyfYWj/8g91B2/m/WOo7KyZxIC918R1/jvBRYQGreDL+yutb1ReGc6eUHX+NKJIYqzfal+PY7tGotS1Srn9WhBspXq8/0rNsEnsSoA="}],"Additional":[]}

IP 位址網址的 TLS 1.3 和 SNI

TLS 1.3 要求用戶端提供伺服器名稱識別 (SNI)。

SNI 擴充功能會將 SNI 資訊指定為 DNS 網域 (而不是 IP 位址):

「HostName」包含伺服器的完整 DNS 主機名稱,如用戶端可理解的值。主機名稱以位元組字串表示,使用 ASCII 編碼且不含半形句號。這可讓您使用 RFC5890 中定義的 A 標籤,支援國際化網域名稱。DNS 主機名稱不區分大小寫。RFC5890 的第 2.3.2.4 節說明用來比較主機名稱的演算法。

「主機名稱」中不允許使用通用 IPv4 和 IPv6 位址。

想要利用傳輸層安全標準 (TLS) 1.3 安全性改善的 DoH 或 DoT 應用程式,可能難以滿足這些需求。Google 公用 DNS 目前接受未提供 SNI 的 TLS 1.3 連線,但未來為了營運或安全性方面的考量,我們可能需要變更這項連線。

關於 SNI 的 DoT 或 DoH 應用程式建議如下:

  1. 針對連至 Google 公用 DNS DoT 或 DoH 服務的任何連線,請傳送 dns.google 主機名稱做為 SNI。
  2. 如果沒有可用的主機名稱 (例如,在執行機會式 DoT 的應用程式中),最好在 SNI 中傳送 IP 位址,而不要留空。
  3. IPv6 位址應以 Host 標頭的 [2001:db8:1234::5678] 括號形式顯示,但 SNI 中沒有括號。

DNS 回應截斷

雖然 Google 公用 DNS 通常不會截斷對 DoT 和 DoH 查詢的回應,但會採取下列兩種做法:

  1. 如果 Google 公用 DNS 無法取得授權名稱伺服器的完整且未經截斷的回應,就會在回應中設定資訊公開和同意聲明 (TC) 標記。

  2. 如果 DNS 回應 (以二進位 DNS 訊息格式) 超過 TCP DNS 訊息的 64 KiB 限制,如果 RFC 標準要求這麼做,Google 公用 DNS 可能會設定 TC (截斷) 標記。

不過在這些情況下,用戶端不需要使用純 TCP 或任何其他傳輸來重試,因為結果會是相同的。