背景

作为普通人上网的第一步,DNS 是极为重要的一环。而在网络发展的初期,并没有考虑到会有像 GFW 这样罪恶无耻的东西出现,因此 DNS 设计的比较脆弱,也因此留下了重大的隐患。

原理

我们知道,在上网的时候为了方便我们记忆,在地址栏中输入的都是域名,而在真正的网络中,是通过 IP 地址组织起来的,因此需要将域名转换成对应的 IP 地址,这就是 DNS 的作用。

dns-function

但是 DNS 当初的实现并没有考虑到太多的情况,因此 DNS 的协议是 UDP,是一种无连接的传输协议,并且也没有响应的加密规则。因此在实际使用的过程中,会非常容易收到污染或者篡改。一般常见的手段有以下几种。

DNS 污染

一般普通人在上网的时候,并不关注 DNS 服务器的设置,因此一般都是默认的当前 ISP 的 DNS。而在中国,因为 ISP 和其主子的沆瀣一气,会将某些不希望你访问的网站的域名故意指到错误的 IP 地址上去,从而阻止你正常访问该网站。

例如,我们要解析一下 Google 的域名,默认使用当前 ISP 提供的 DNS。

$ dig www.google.com

; <<>> DiG 9.10.6 <<>> www.google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37014
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;www.google.com.			IN	A

;; ANSWER SECTION:
www.google.com.		136	IN	A	31.13.82.17

;; Query time: 34 msec
;; SERVER: 202.106.0.20#53(202.106.0.20)
;; WHEN: Wed Jan 29 09:37:02 CST 2020
;; MSG SIZE  rcvd: 48

dns-wrong-response

看起来好像没有什么问题,一切都正常,但是我们去查询这个解析过的 IP 地址的话就会发现,这个 IP 实际上是 Facebook 的(不同的时间不同的地点解析出来的结果并不相同),因此如果我们去这个服务器访问 Google 的服务是不可能成功的。

DNS 劫持或抢答

在饱受 ISP 提供的默认 DNS 服务器的折磨以后,一些水平高一些的上网用户决定不在使用这些垃圾 DNS,转而使用一些例如 8.8.8.8 这些国外大公司提供的 DNS。然而问题依旧存在。

下面我们指定使用 Google 的 8.8.8.8 DNS 服务器来解析域名。

dig @8.8.8.8 www.google.com

; <<>> DiG 9.10.6 <<>> @8.8.8.8 www.google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2913
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;www.google.com.			IN	A

;; ANSWER SECTION:
www.google.com.		180	IN	A	8.7.198.45

;; Query time: 44 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Wed Jan 29 09:23:24 CST 2020
;; MSG SIZE  rcvd: 48

dns-hijacking

我们可以看到,很奇怪的是我们竟然收到了 3 条 DNS 的应答,而且每条返回的 IP 地址都不一致。然后我们在逐一查询这三个 IP 地址,发现只有最后一个 IP 地址是真正的 Google 服务器的地址,而前两个又指向了奇奇怪怪的地方。而这种行为,被称作 DNS 抢答或者劫持。其原理是 ISP 利用了 UDP 的明文不加密的特性,检查了我要解析的域名,发现域名在其黑名单上后,在真正的 8.8.8.8 DNS 响应之前,抢先返回了假的 IP 地址。而由于 DNS 客户端的工作原理,本地计算机只认可第一条返回的数据,即这条假的 IP 地址,因此导致再次无法访问 Google 服务器。

应对措施

我们可以总结一下,之所以 DNS 的环境被干扰成功,主要在于 DNS 请求和应答都是明文而非加密的,因此要避免干扰就需要将 DNS 请求过程加密。目前有两种主流的加密技术。

两者有点类似,都是用来加密 DNS 请求流量。IETF 已将 https 上的 DNS 定义为 RFC8484,并将其通过 TLS 定义为 RFC7858 和 RFC8310。DoT 使用 tcp 作为基本连接协议,并通过 tls 加密和身份验证进行分层。DoH 默认端口是 443,基于 https 协议;DoT 默认端口是 853,基于 tcp 协议。

DoT

全称是 DNS over TLS,是基于 tcp 传输层协议的,默认端口是 853。因此在具体使用中也很容易被 GFW 干扰,只要重点监控和屏蔽掉 853 端口的流量即可达到目的。

DoH

全称是 DNS over HTTPS,是基于 https 应用层协议的,从而让 GFW 无法区别于正常的 https 流量,但是目前提供该服务的站点还是比较少,最出名的就是 Cloudflare1.1.1.1

大概的原理就是自己在本地起一个微型 DNS 服务器只供自己使用,在本机的网络设置中设置 DNS 服务器的地址就是 127.0.0.1。然后在具体查询域名的时候,本地 DNS 服务器通过 https 协议向真正提供 DoH 服务的服务器发送加密后的解析请求,因为请求是加密的,目标端口也是和 https 用的一样的 403,ISP 就无法得知具体的内容,也无法像一开始一样进行污染或抢答了。

doh

其中,比较常用的 DoH 工具是 DNSCrypt

但是因为目前提供该服务的服务器很有限,因此也很容易受到干扰和屏蔽,只要把有限的这几个提供服务的站点屏蔽掉就好了。

其他情况

iOS

在我使用 iPhone 的时候发现一个问题,就是如果使用 wifi 上网,用这个 iPhone 可以正常翻墙。但是使用蜂窝网络的情况下就无法翻墙,然后将其作为热点,电脑可以翻墙。也就是说在本机使用蜂窝网络的条件下,某个翻墙环节出现了问题。后来查阅了资料才知道,正常情况下,如果使用蜂窝网络上网,DNS 服务器默认就是使用当前运营商的服务器,因此还是受到了 DNS 污染或者劫持。后来使用了别人配置好的 Shadowrocket 的配置规则,就解决了这个问题。通过观察这个规则,可以看出,有一些被污染的域名在后面配置了 force-remote-dns 选项,强制要求进行远程 DNS 域名解析,规避了被运营商 DNS 污染和抢答的问题。

参考资料和工具