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 致力于为 DoH 和 DoT 客户端提供快速、私密且安全的 DNS 解析。

支持的 TLS 版本和加密套件

Google 公共 DNS 支持 DoH 和 DoT 的 TLS 1.2 和 TLS 1.3;不支持早期版本的 TLS 或 SSL。仅支持采用正向安全加密算法和基于额外数据的身份验证加密 (AEAD) 的加密套件。Qualys SSL Labs 显示了当前受支持的一组加密套件。

Endpoints

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 请求的正文是二进制 UDP DNS 载荷,其内容类型为 application/dns-message。
    • GET 的网址为 https://dns.google/dns-query?dns=BASE64网址_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 客户端,但这些客户端通常需要中等的技术配置。

命令行示例

以下命令行示例并非用于实际客户端,仅仅是使用常用的诊断工具提供的图示。

以下命令需要使用 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

根据 RFC 8484 第 4.1 节的建议,此命令中的 Base64Url 编码字符串是 dig +noedns example.test A 发送的 DNS 消息,DNS 字段设置为零。shell 命令使用 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

这使用适用于 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 地址应以 [2001:db8:1234::5678] 包围形式显示在 Host 标头中,但 SNI 中不得包含方括号。

DNS 响应截断

虽然 Google 公共 DNS 通常不会截断对 DoT 和 DoH 查询的响应,但存在以下两种情况:

  1. 如果 Google 公共 DNS 无法从权威域名服务器处获取完整响应和未截断响应,它会在响应中设置 TC 标志。

  2. 如果 DNS 响应(二进制 DNS 消息形式)超过 TCP DNS 消息的 64 KiB 限制,则 Google 公共 DNS 可能会设置 TC(截断)标志(如果 RFC 标准要求这样做)。

但在这些情况下,客户端无需使用普通 TCP 或任何其他传输来重试,因为结果会一样。