DNSSEC与DoH/DoT/DNSCrypt/DNSCurve

本文基于署名-非商业性使用 4.0 国际 (CC BY-NC 4.0)发布,转载请注明出处。

以下内容如有错误还请指正,非常感谢。

2019-06更新,更正错误

我曾经有过这样的疑问,为什么即便域名正确配置了DNSSEC后还能被污染呢?

既然已经有了DNSSEC我们为什么还需要DoH/DNSCrypt呢?

TL;DR

DNSSEC是去签名信息,保护的是DNS ZONE信息的完整性(即消息不可被敌手篡改而不被我们发现,但是消息本身是明文的),通过验证签名让用户确信 ZONE 里的信息(DNS 记录)的确是来自于域名的所有者而未被篡改。
而DoH/DNSCrypt则相当于加密,保护的是从递归DNS服务器到DNS客户端的机密性(即消息本身无法被敌手读取),而这个加密的过程是否提供额外的保护完整性的机制取决于使用的密码算法(请注意,即使密码算法本身提供完整性保护,这里的完整性保护也仅限于递归DNS服务器和DNS客户端之间,递归服务器是否可信,以及DNS ZONE的name server到递归服务器之间的通信是否安全是不确定的)
类似但不同于 DoH/DNSCrypt, DNSCurve 提供DNS ZONE NS 到递归服务器之间的加密

下面为了方便理解基于的假设是这里采用的密码算法完全不提供完整性保护,只是简单的加密,和实际情况还是有一些区别的。

完整版

打一个简单的比方,如果我们把下载软件安装包的过程比作DNS查询的过程

那么使用 DoH/DNSCrypt 去查询DNS就相当于通过https去下载未签名的软件安装包,而下载好的程序是不是真的来自原始的开发者用户并没有足够的信心。

而DNSSEC相当于给这个软件的安装包加上了一个数字签名。但是如果只有DNSSEC本身的话那么这个软件包就是通过http来传输的。中间人知道用户在下什么东西,也可以随便修改这个软件包。但是用户收到数据之后一校验数字签名就能知道他收到的数据不是发送者发出的原始数据,是被改过的。但由于数据是明文传输的,中间人可以伪造出一个修改过并且不带数字签名的软件安装包完全替换掉发送者发出的那个。或者干脆阻止这个连接,破坏可用性。

最优的方案就是ZONE配置了 DNSSEC 的签名,用户到递归服务器之间的通信由 DoH/DNSCrypt 保护,递归服务器到权威服务器(NS)之间的通信由 DNSCurve 保护。

墙污染DNS(假设域名正确配置了DNSSEC)的原理:

先不考虑国内公共DNS有几家是支持DNSSEC的,假设都支持DNSSEC好了。

下面我们来模拟一下DNS查询的过程

正常情况(未被污染的域名):
root@xxx:~# dig paypal.com  @8.8.8.8 +dnssec +trace

; <<>> DiG 9.9.5-9+deb8u13-Debian <<>> paypal.com @8.8.8.8 +dnssec +trace
;; global options: +cmd
.			39575	IN	NS	a.root-servers.net.
.			39575	IN	NS	b.root-servers.net.
.			39575	IN	NS	c.root-servers.net.
.			39575	IN	NS	d.root-servers.net.
.			39575	IN	NS	e.root-servers.net.
.			39575	IN	NS	f.root-servers.net.
.			39575	IN	NS	g.root-servers.net.
.			39575	IN	NS	h.root-servers.net.
.			39575	IN	NS	i.root-servers.net.
.			39575	IN	NS	j.root-servers.net.
.			39575	IN	NS	k.root-servers.net.
.			39575	IN	NS	l.root-servers.net.
.			39575	IN	NS	m.root-servers.net.
.			39575	IN	RRSIG	NS 8 0 518400 20170823170000 20170810160000 15768 . GTkQYZZf/MnllTvZFy6y0GTd516lSYJUvNP/oyL4TWDiKcPUy7q8Wn9A yaz6RmnnrB/qx+pEAH9Mg+OHOKRizBc8GRNSEK8YiNotoN3RwO3wAfhb uAkfc60TvM82vav+VERAnICi9UA8kn1J63+/8Av1zoU1gnLqs+c400Y8 fg5qIoBuwdhcNRgvW8acHFQ1HXiAno9OSqmftb1jYL5AyXhmj1k8h6Ec /+1bmPobSBe8n6CrdBLYz80i3KNO1CJIHunETQKopj+YAiQTLqkZRguw 0FYWbD0y9EercWimNHTqyRa/PrU126Jr7IIT9BEcUjX0qE2XMrX4mOPJ 7gFr0A==
;; Received 525 bytes from 8.8.8.8#53(8.8.8.8) in 392 ms

com.			172800	IN	NS	a.gtld-servers.net.
com.			172800	IN	NS	b.gtld-servers.net.
com.			172800	IN	NS	c.gtld-servers.net.
com.			172800	IN	NS	d.gtld-servers.net.
com.			172800	IN	NS	e.gtld-servers.net.
com.			172800	IN	NS	f.gtld-servers.net.
com.			172800	IN	NS	g.gtld-servers.net.
com.			172800	IN	NS	h.gtld-servers.net.
com.			172800	IN	NS	i.gtld-servers.net.
com.			172800	IN	NS	j.gtld-servers.net.
com.			172800	IN	NS	k.gtld-servers.net.
com.			172800	IN	NS	l.gtld-servers.net.
com.			172800	IN	NS	m.gtld-servers.net.
com.			86400	IN	DS	30909 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CF C41A5766
com.			86400	IN	RRSIG	DS 8 1 86400 20170826050000 20170813040000 15768 . USlIstszud1aon+oclR6Pa8hZ5evhy3giQwiPpqK+Rm61+e2hJb3g9xT hRV37bLez4S1LBT1sBUFSDN65AaorB12Muq/J0bnpt9J4oyTooUQhmbi aP+bUJtLpiw14OKyydvgwEJQc5qXz6E9nsNT9GRNGwMNp7yIkY8laqql 4pwaPnbGn3x4hKN/VWWJ5sf4q5TOL2BxG5PRIxKoiGRYmC1xJvzORyca vbf17BGDQdYM7MUcohHiRrwz8YZHqV8Guyhwcf2MJK06rCePBZLskfah Lde953aHAvEZxw47Tt7J7FAKNRR6brN2zFKNf5NBQ8r2KPUO2qd9eDxp EoBfOQ==
;; Received 1170 bytes from 193.0.14.129#53(k.root-servers.net) in 459 ms

paypal.com.		172800	IN	NS	pdns100.ultradns.net.
paypal.com.		172800	IN	NS	pdns100.ultradns.com.
paypal.com.		172800	IN	NS	ns1.p57.dynect.net.
paypal.com.		172800	IN	NS	ns2.p57.dynect.net.
paypal.com.		86400	IN	DS	21037 5 2 0DF17B28554954D819E0CEEAB98FCFCD56572A4CF4F551F0A9BE6D04 DB2F65C3
paypal.com.		86400	IN	RRSIG	DS 8 2 86400 20170817041651 20170810030651 5528 com. Jrg6iI+9lcBwHdLlMH5O8SS6AMR8lVfm8JUszO5d73R+h6T6Ist7TQz5 iCFPRANi+b7Jmt8AimdGINWie+VUHOfR7fyU91kyi+aOVeITTcv55JEH fWSEzatoXrEzx9r8JgosBQr4fJnzc/OxmyrzNky5krJGRfwkcgmW0Anq Qtk=
;; Received 482 bytes from 192.43.172.30#53(i.gtld-servers.net) in 794 ms

paypal.com.		300	IN	A	64.4.250.33
paypal.com.		300	IN	A	64.4.250.32
paypal.com.		300	IN	RRSIG	A 5 2 300 20170827105805 20170728101420 11811 paypal.com. t+cm9NJYN77Qyci2Vij6Bez/8NY1WLHV51jhBDPPP4gk9w2m+3YB8XID XLXlrUt3X96af27sUrWfNkEIo68nO8/c5A04ubRTXlLfcHfyaTov5hPv 71Zwo0Yau8gu8Qdww1x61LLvGoTqxXuaQwYSRf09pjO36UVzxa5Fj84p Tz4=
paypal.com.		300	IN	NS	pdns100.ultradns.net.
paypal.com.		300	IN	NS	ns2.p57.dynect.net.
paypal.com.		300	IN	NS	ns1.p57.dynect.net.
paypal.com.		300	IN	NS	pdns100.ultradns.com.
paypal.com.		300	IN	RRSIG	NS 5 2 300 20170910052028 20170811051656 11811 paypal.com. sdyQ3dPuWld6WVgFUst8NoeTRzxZgx1GhM/lKTOdkB9oDqrNkHDw4K2w q2LonMSwNi+Q/UE7r33+lKxa5YbFTMWUaT5lAI9VzTNwJ4FOXFdRBb3A CN1oaadDND0oHBAWxm5dMCRozf6zycpEpat6QJmk+AzdhhuYGRqTNboB JHE=
;; Received 523 bytes from 204.13.250.57#53(ns2.p57.dynect.net) in 5 ms

可以看到开启DNSSEC之后响应信息里多出了RRSIG记录和DS记录,简单的说RRSIG记录里保存了其他记录(RRset)的签名信息,而父区域的DS记录保存了子区域DNSKEY的哈希值用来维持信任链。

以上面这个解析paypal.com的流程为例的话,简化的信任链如下:

受信的.com的DS记录(In the Root Zone) =验证=> .com的公钥(DNSKEY) =验证=> RRSIG =验证=> paypal.com的NS记录 + DS记录 =验证=> paypal.com的公钥(DNSKEY) =验证=> RRSIG =验证=> paypal.com的A记录

更具体的实现细节比较复杂,比如DNSKEY还分为KSK和ZSK,Root Zone是如何签名的等等...

这里就不展开了。

中国特色的情况:

墙的DNS污染大致采取了这种策略:

对于经过国际出口的DNS请求包,一律伪装成根服务器直接给出伪造的响应,不管发起请求的主体是用户自己还是用户设置的墙内的DNS递归服务器(运营商的、还有那些公共DNS)。
; <<>> DiG 9.11.2 <<>> fuckgfw.org @8.8.8.8 +dnssec +trace
;; global options: +cmd
.                       220397  IN      NS      a.root-servers.net.
.                       220397  IN      NS      b.root-servers.net.
.                       220397  IN      NS      c.root-servers.net.
.                       220397  IN      NS      d.root-servers.net.
.                       220397  IN      NS      e.root-servers.net.
.                       220397  IN      NS      f.root-servers.net.
.                       220397  IN      NS      g.root-servers.net.
.                       220397  IN      NS      h.root-servers.net.
.                       220397  IN      NS      i.root-servers.net.
.                       220397  IN      NS      j.root-servers.net.
.                       220397  IN      NS      k.root-servers.net.
.                       220397  IN      NS      l.root-servers.net.
.                       220397  IN      NS      m.root-servers.net.
.                       220397  IN      RRSIG   NS 8 0 518400 20170825170000 201
70812160000 15768 . Ryet1o4jMlZyGwwwUo7HQ/hxnhm2PlLhAyMhHEWyo89M9ZfbModSEVYs WMn
epoIcuU/Ng5VL+TxdrxiSYeE065u69xsQmFy6kT8ozKCcWmyOkq4d udKLZCc+RA6JuUxpZE5in/fgJp
I03r8/YkkUOZP4lDqhyiDKnJm5fyAP h6912m5YAOM5Rx2hz0lYh2BjLO9vJ74h/6Y4bGoOvHG+etkYs
NFq4tE/ praTqBschffzR7++kNYdSXvY2OI/gwNMofJAJP8HUPRlgnmF845WKp8q Fo7+uwpZvBK8Suk
gVxlrAgZspR6AbLRKYMcDNigo3Ycw66FKjGuUklUX b5ZZ+A==
;; Received 525 bytes from 8.8.8.8#53(8.8.8.8) in 105 ms

fuckgfw.org.            3413    IN      A       93.46.8.89
;; Received 56 bytes from 192.203.230.10#53(e.root-servers.net) in 28 ms

上面就是下文提到的墙对于递归服务器伪装成根服务器

对于递归服务器本身而言,墙会伪装成根服务器或者顶级域(TLD)的服务器直接给出被污染域名的响应,让稍后到达的正确响应被丢弃,这样就实现了DNS缓存投毒。当然,攻击者也只能伪造出未签名的响应,所以如果DNS客户端或者递归服务器强制校验签名(要么得到正确结果,要么直接查询失败)的话就会拒绝掉这个伪造的响应。但如果客户端或者递归服务器要这么配置,那所有未签名未配置DNSSEC的域都无法解析。考虑到DNSSEC的普及程度,这样做=。= 而且只是拒绝了错误结果,并不能保证获得正确的结果(比如可以直接把你明文的请求或者响应丢掉)。所以在这种中国特色的环境下DNSSEC显得比较鸡肋。

还有就是即使输入一个并不存在的DNS服务器,还是会有响应。就像这样:

C:\Users\Minty>nslookup 1.blog.fuckgfw233.org 8.8.8.9
DNS request timed out.
    timeout was 2 seconds.
服务器:  UnKnown
Address:  8.8.8.9

非权威应答:
名称:    1.blog.fuckgfw233.org
Addresses:  200:2:f3b9:bb27::
          93.46.8.89

C:\Users\Minty>nslookup fuckgfw.org 8.8.8.88
DNS request timed out.
    timeout was 2 seconds.
服务器:  UnKnown
Address:  8.8.8.88

非权威应答:
名称:    fuckgfw.org
Addresses:  200:2:253d:369e::
          243.185.187.39

总而言之,DNSSEC可以让用户验证查询结果的完整性(防篡改),就像数字签名一样。但是它没有加密返回的查询结果,所以中间人能知道用户在查什么,也可以把这个带签名的结果用一个普通的结果替换掉或者丢掉.所以能被轻松污染...

参考资料:

https://www.icann.org/resources/pages/dnssec-qaa-2014-01-29-zh

https://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D%E7%B3%BB%E7%BB%9F%E5%AE%89%E5%85%A8%E6%89%A9%E5%B1%95

https://tools.ietf.org/html/rfc4033

https://tools.ietf.org/html/rfc4034

https://tools.ietf.org/html/rfc6840

https://msdn.microsoft.com/zh-cn/library/jj200221.aspx

https://www.cloudflare.com/dns/dnssec/how-dnssec-works/

https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Securing_DNS_Traffic_with_DNSSEC.html