本文为科普向,文字描述较为浅显易懂,不适合想要深入了解的读者。
Get Started
在了解 DNS 劫持是怎么进行之前,我们需要先了解 DNS 是怎么进行的。这里回到一道经典的面试题:
当你在浏览器里输入 www.baidu.com 时做了哪些事情?
这道题之所以经典,是因为千人千面,你能从「前端」、「后端」、「运维」、「Devops」这几种工种中得到截然不同的答案。
而今天讨论的就是这个问题的第一步:「DNS」。
考虑到这是一篇科普向的文章,我们用一句话解释下 「What is DNS」。
DNS 可以看做一个 domain to IP 的黄页,你告诉它域名,它给你对应的 IP 地址。
那么下一个问题:「DNS 是怎么运作的」。
如图所示,一个用户发起请求后经过浏览器和 ISP 的 DNS 解析器(英文通常是 DNS resolver 或者 Local DNS),然后经由「根域名服务器」、「TLD 域名服务器」和「权威域名服务器」,最终拿到一个域名的门牌号。
下一个问题是,图中的每个节点到底是拿来做什么的呢?
域名解析绑定
在完全开始之前,一次域名购买到解析的经历能帮助你更好的理解这一切。以 cloudflare 为例,假设你的域名放在 cloudflare 解析,那么 cloudflare 会为你提供两个 NS 记录:
并且告诉你将这俩 NS 记录绑定到你的域名上。
绑定成功并验证通过后,你就可以进行下一步操作,也就是进行记录绑定了:
节点解析
简单消化一下,上面的插叙并非突发奇想,而会构成你理解下面步骤的重要部分。
根据上图中讲到的步骤,我们用更拟人的方式来解释每一步:
- 用户键入了「codesky.me」这个地址后,首先先问本地的 DNS 记录(你在 Network 的 DNS 设置里能看到对应的 IP,通常第一跳指向路由器),DNS 记录转发去路由器,路由器中下一跳的 IP 地址。路由器说:「你等等,我去帮你问问 ISP,你这个域名的门牌号是什么」。(图中步骤 1)
- ISP 收到了请求,但它也不知道,不过作为一个优秀的中间商,它选择问出来了再告诉你。「让我先去挨个问问」ISP 这么想。(图中步骤 2)
- 于是它先找了根域名服务器,毕竟现在他还没有任何线索,根域名服务器说,「.me 啊,你去找 .me TLD 域名服务器吧,这是他的名片」。(图中步骤 3、4)
- 于是 ISP 又找到了 TLD 域名服务器,TLD 域名服务器说:「codesky.me 啊,它在我这里登记的 NS 记录是这个,你去那边找他吧」(图中步骤 5、6)
- 好不容易,ISP 总算是到了「权威域名服务器」处(比如上文我们绑的 cloudflare),权威域名服务器吭哧吭哧的找到对应的 A 记录,返回给 ISP。ISP 总算使命必达了。(图中 7、8)
- ISP 拿着对应的 IP 回复路由器,路由器再回复给用户,这下,你总算能访问「codesky.me」了。(图中的 9、10)
然而,如果你绑定的不是一条 A 记录,而是 Cname:
那么可能「DNS 解析器」又要重新跑一遍了。
而实际上,我们会发现,本图中的步骤数字有些特殊之处------ ISP 自己全问了一遍,而用户到路由器、路由器到 ISP 只是在躺平等通知,这就是两类查询类型:「迭代查询」和「递归查询」。
节点缓存
当然,倒也不用每次都这么辛苦,毕竟「一切能被缓存的东西终将被缓存」。拿你的这一次访问来说,实际上浏览器第一步会先问问自己:「我有缓存吗?」有的话我就不挣扎了(Chrome 浏览器缓存可见:chrome://net-internals/#dns)
如果没有,Chrome 在进行一次系统调用,让操作系统干活。但操作系统说:「且慢,让我来看看我有没有缓存」。
等到操作系统也确认自己没有缓存了,他再去路由器里问(下面同理不再赘述,台词不好编)。
当然,并不需要完全命中也行:假设查完 dashboard.var.moe,他指向了一个 CNAME f.var.moe,但 f.var.moe 之前已经查询过了,那也是可以直接用的。
所以劫持是什么
这里我们说的劫持更多的是个中性词,因为它不止能违法乱纪,还能做些特殊的引流操作。
过去我们往往听到的词是「运营商(ISP)劫持」,运营商个个是人才,说话又好听,可以看到,上文的链路中一直是经过 ISP 的,ISP 自然也是希望降本提效的,因此他会尽可能的缓存下来结果,方便下一次更快的返回,但黑心商户做的可能不止这么多,他们甚至可能会缓存整个网页信息,直接指向一个缓存后的网页而并非你的源站,大聪明觉得:这样我不是更省钱了?你还会觉得访问特别快。
病毒或者木马的劫持是另一种,同样都是基于缓存「直接返回」做的恶。
而在公司内部我们提到劫持的时候,就显得单纯不少了,我们只是在第一跳中将某某域名强制绑定在某个 IP 中,比如在办公网环境下绑定了一个 1.2.3.4 到 codesky.me,那么我们向公司的路由器(或者交换机,或者上游解析服务器)请求时,「DNS 解析器」直接说「不用找了,去 1.2.3.4」。而如果在服务器机房内调用时,由于不经过对应的解析服务器,所以就会按照你绑定的记录正常访问。
为什么劫持有时候会失败
从上文中我们知道了为什么我们会需要分开来挨个配置不同场景的 DNS 劫持:「DNS 解析器不一样」。
那么失败又是为什么呢?试想一下这样的场景:
公司今天新买了一批用来做办公网访问的设备,小明配置好了,能联网了,但是忘了还要更新劫持清单。
「忘了」,就是最简单的理由,也就是设备列表没理清楚,少更新了。
如何体验:自己搭建一个 DNS 服务器
可以参考 2017 年我曾经写过的一篇文章(实际上我自己都忘了):[翻译]在 macOS 使用 Dnsmasq 进行本地开发,你可以通过 dnsmasq 体验一个「DNS 解析器」的配置与运行。
附录
DNS Records
上述我们所提到的 NS、A 记录以及 CNAME,如果是初次接触 DNS 的读者一定会疑问:这到底是什么东西,实际上 DNS 记录不只有上面说的几种,但常用的就那么几个,简单介绍一下:
- A 记录:保存域名 IPV4 门牌号。
- AAAA:保存 IPV6 门牌号。
- CNAME:你去找谁(域名)问。
- NS:谁是权威。
当然,A 和 AAAA 可以不止一条,他会以循环的方式以此请求,以便进行多 IP 的负载均衡。
另外:iOS 一直在实验 HTTPS 记录,它还是一个 rfc:www.rfc-editor.org/rfc/rfc9460...
用于传递更多信息,比如:
css
example.com 3600 IN HTTPS 1 . alpn="h3,h2" ipv4hint="192.0.2.1" ipv6hint="2001:db8::1"
这一记录中表明支持的 HTTP 版本和 ipv4、ipv6 的地址,设备会同时发起对 A / AAAA / HTTPS 的请求,如上文 HTTPS 记录中的信息表示齐全,那么就不会使用 A / AAAA 的内容了。
Reference
-
What is DNS - Cloudflare:www.cloudflare.com/zh-cn/learn...
-
全面了解移动端DNS域名劫持等杂症:原理、根源、HttpDNS解决方案等:www.52im.net/thread-2121...
-
DNS 基本概念:help.aliyun.com/document\_d...
-
Speeding up HTTPS and HTTP/3 negotiation with... DNS:blog.cloudflare.com/speeding-up...