4. DNS协议深度分析:递归与迭代查询、DNS缓存投毒、EDNS0扩展
DNS,说白了就是互联网的电话本。你输入一个域名,它帮你找到对应的IP地址。听起来简单,但里面的门道可不少。我做了这么多年网络排障,DNS问题占了至少三成。今天咱们就把它彻底聊透。
4.1 递归查询 vs 迭代查询
这两种查询方式,是DNS工作的核心逻辑。很多朋友分不清,其实看一次抓包就懂了。
4.1.1 递归查询
客户端发一个请求给DNS服务器,说:“帮我查一下 www.example.com 的IP。” 服务器要么返回结果,要么返回错误。它不会让你再去问别人——这就是递归。
我习惯把递归查询比作「甩手掌柜」。你只管把问题丢出去,等着拿答案就行。中间它问了谁、查了几个服务器,你一概不知。
关键特征:
- 客户端只发一次请求
- 服务器必须返回最终结果
- 通常用于客户端到本地DNS服务器
4.1.2 迭代查询
迭代查询就不一样了。服务器告诉你:“我不知道,但你可以去问别人。” 然后给你一个 referral(指引)。
举个例子。你问本地DNS服务器:“www.example.com 在哪?” 它说:“我不知道,你去问根服务器。” 你问根服务器,它说:“去问 .com 顶级域服务器。” 你问 .com 服务器,它说:“去问 example.com 的权威服务器。” 最后你问权威服务器,拿到IP。
你看,每一步都是「你」在主动问。服务器只负责指路,不帮你跑腿。
| 对比项 | 递归查询 | 迭代查询 |
|---|---|---|
| 客户端工作量 | 低(只发一次) | 高(多次请求) |
| 服务器负载 | 高(要查到底) | 低(只给指引) |
| 典型场景 | 客户端 → 递归器 | 递归器 → 权威服务器 |
| 抓包特征 | 单次查询 + 单次响应 | 多次查询 + 多次 referral |
4.1.3 Wireshark中怎么看
打开Wireshark,过滤 dns。你会看到两种模式:
- 递归查询:Flags字段中 Recursion Desired (RD) 位为1,Recursion Available (RA) 位也为1。
- 迭代查询:响应中不包含答案,而是包含 Authority 和 Additional 记录——这就是 referral。
# 递归查询抓包示例
No. Time Source Destination Protocol Info
1 0.000 192.168.1.2 8.8.8.8 DNS Standard query 0x1234 A www.example.com
2 0.045 8.8.8.8 192.168.1.2 DNS Standard query response 0x1234 A 93.184.216.34
# 迭代查询抓包示例(referral)
No. Time Source Destination Protocol Info
1 0.000 192.168.1.2 198.41.0.4 DNS Standard query 0x5678 A www.example.com
2 0.030 198.41.0.4 192.168.1.2 DNS Standard query response 0x5678 - No such name
Authority: . NS a.root-servers.net
Additional: a.root-servers.net A 198.41.0.4
嗯,这里要注意。如果你看到响应里没有 Answer RRs,但 Authority 和 Additional 有内容——那就是 referral,典型的迭代查询。
4.2 DNS缓存投毒
这是DNS领域最危险的攻击之一。我当年在ISP干的时候,遇到过一起大规模断网事件,最后定位就是缓存投毒。
4.2.1 原理是什么
攻击者伪造DNS响应,往递归服务器里塞入错误的记录。用户访问正常网站,却被指向钓鱼服务器。
说白了,就是让DNS服务器「记住」错误的信息。下次有人问,服务器直接返回假的IP。
我曾经踩过的坑: 有一次客户反馈访问银行网站跳转到赌博页面。我查了半天,发现是本地路由器上的DNS被篡改了。攻击者利用UPnP漏洞,把路由器的DNS服务器地址改成了恶意服务器。从那以后,我所有项目的第一件事就是锁死DNS配置。
4.2.2 攻击手法
- ID欺骗:DNS查询的Transaction ID是16位,只有65536种可能。攻击者批量发送伪造响应,总有一个ID能对上。
- 端口随机化绕过:现代DNS服务器使用随机源端口。攻击者需要同时猜对ID和端口,难度大增。
- Kaminsky攻击:2008年Dan Kaminsky发现的经典漏洞。攻击者不直接投毒目标域名,而是投毒NS记录,把整个域的查询都引向恶意服务器。
| 攻击类型 | 难度 | 影响范围 | 防御手段 |
|---|---|---|---|
| ID欺骗 | 低 | 单个域名 | 随机源端口 |
| 端口随机化绕过 | 中 | 单个域名 | DNSSEC |
| Kaminsky攻击 | 高 | 整个域 | DNSSEC + 源端口随机化 |
4.2.3 Wireshark检测方法
你可以在Wireshark里设置过滤条件:
# 检测可疑的DNS响应
dns.flags.response == 1 and dns.count.answers > 0 and not dns.flags.authentic_data
# 检测多个相同查询的响应(可能是暴力攻击)
dns.qry.name == "www.example.com" and dns.flags.response == 1
我个人习惯的做法是:先看响应时间。正常DNS响应一般在50ms以内。如果某个响应时间异常短(比如1ms),而且来自非权威源——那就要警惕了。
避坑指南: 我曾经在分析一起投毒事件时,发现攻击者伪造的TTL设得特别大(86400秒)。这样即使发现投毒,缓存也要一天才能过期。所以我现在看DNS抓包,第一眼就看TTL。异常大的TTL + 非权威源 = 大概率有问题。
4.3 EDNS0扩展
EDNS0,全称是Extension Mechanisms for DNS。你想想看,最初的DNS协议是1987年设计的,那时候谁能想到今天的互联网规模?
4.3.1 为什么需要EDNS0
传统DNS报文有512字节的限制。这在80年代够用,但现在一个DNSSEC记录就能轻松超过这个数。EDNS0通过OPT伪资源记录,扩展了UDP报文大小,支持到4096字节甚至更大。
说白了,就是给DNS协议「打补丁」。不改核心协议,但增加扩展能力。
4.3.2 EDNS0的核心能力
- UDP报文大小扩展:从512字节扩展到4096字节
- DNSSEC支持:携带DNSSEC相关的OK位和签名记录
- 客户端子网(ECS):CDN可以根据客户端IP返回最近的节点
- DNS Cookie:防止DNS放大攻击和缓存投毒
4.3.3 Wireshark中识别EDNS0
在Wireshark里,EDNS0表现为Additional records中的OPT记录。过滤方法:
# 过滤包含EDNS0的DNS报文
dns.opt
# 查看EDNS0版本
dns.opt.version
# 查看支持的UDP大小
dns.opt.udp_payload_size
抓包示例:
Domain Name System (response)
Transaction ID: 0x9abc
Flags: 0x8180 Standard query response, No error
Questions: 1
Answer RRs: 1
Authority RRs: 0
Additional RRs: 1
Additional records
<Root>: type OPT (41)
Name: <Root>
Type: OPT (41)
UDP payload size: 4096
Extended RCODE: 0
Version: 0
Flags: 0x00
Data: 00
4.3.4 实际项目中的坑
我记得有一次,客户说某些地区的用户访问网站特别慢。我抓包一看,发现DNS响应被截断了——中间防火墙把超过512字节的UDP报文直接丢了。
EDNS0虽然支持大报文,但很多老旧设备不支持。解决方案有两个:
- 在DNS服务器上强制使用TCP回退(当响应超过512字节时)
- 升级中间网络设备,确保支持EDNS0
注意: 有些防火墙会直接丢弃带有OPT记录的DNS报文。如果你发现某些客户端无法解析域名,先检查中间设备是否支持EDNS0。我建议在DNS服务器上开启EDNS0的日志,记录所有协商失败的情况。
4.4 知识体系总览
下面这张图,把本章的核心逻辑串起来了。你一看就明白:
4.5 本章小结
DNS协议看起来简单,但递归与迭代的差异、缓存投毒的危害、EDNS0的扩展能力,每一个都值得深挖。我建议你打开Wireshark,抓一次真实的DNS查询,对照着看一遍。纸上得来终觉浅,绝知此事要躬行。
嗯,今天就到这里。记住:DNS是网络的基础设施,出了问题影响面极大。平时多积累抓包经验,关键时刻才能快速定位。