传统的 DNS 查询和回复通过 UDP 或 TCP 发送,未经过加密,因此会受到监控、欺骗和基于 DNS 的互联网过滤的影响。Google 公共 DNS 等公共解析器对客户端的响应特别容易受到此漏洞的影响,因为消息可能会通过多个网络,而递归解析器和权威域名服务器之间的消息通常会纳入额外的保护措施。
为了解决这些问题,我们于 2016 年推出了 DNS over HTTPS(现称为 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 请求的正文是内容类型为“application/dns-message”的二进制 UDP DNS 载荷。 - 对于 GET 请求,其格式为
https://dns.google/dns-query?dns=
BASE64URL_OF_QUERY。
- 对于 POST,网址只是
JSON API -
https://dns.google/resolve{?name}{&type,cd,do,…}
- JSON API 页面介绍了更多 GET 参数。只有
name
参数是必需的。
- JSON API 页面介绍了更多 GET 参数。只有
客户端
有许多客户端应用使用 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 节所建议)。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 获取
此命令中的 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 节中介绍了用于比较主机名的算法。
“HostName”中不允许使用文字 IPv4 和 IPv6 地址。
对于希望利用 TLS 1.3 中的安全改进的 DoH 或 DoT 应用,可能难以满足这些要求。Google 公共 DNS 目前接受不提供 SNI 的 TLS 1.3 连接,但出于操作或安全原因,我们以后可能需要更改此连接。
对于 DoT 或 DoH 应用,我们针对 SNI 提供的建议如下:
- 对于连接到 Google 公共 DNS DoT 或 DoH 服务的任何连接,均将 dns.google 主机名作为 SNI 发送。
- 如果没有主机名可用(例如,在进行机会性 DoT 的应用中),最好在 SNI 中发送 IP 地址,而不是将其留空。
- IPv6 地址应在
Host
标头中以[2001:db8:1234::5678]
括号形式显示,但在 SNI 中不应包含括号。
DNS 响应截断
虽然 Google 公共 DNS 通常不会截断对 DoT 和 DoH 查询的响应,但存在以下两种情况:
如果 Google 公共 DNS 无法从权威域名服务器获取未截断的完整响应,则会在响应中设置 TC 标志。
如果 DNS 响应(采用二进制 DNS 消息形式)会超出 TCP DNS 消息的 64 KiB 限制,Google 公共 DNS 可能会根据 RFC 标准设置 TC(截断)标志。
但在这些情况下,客户端无需使用普通 TCP 或任何其他传输重试,因为结果是相同的。