DNS 深度解析:一次域名解析背后的递归、缓存与安全
每天打开浏览器输入一个网址,背后都发生着一次(或多次)DNS 解析。它平时安静得让人意识不到存在,出问题时却能让你整个业务「集体失联」。本文从分层架构讲到查询流程、记录类型、缓存体系与安全机制,最后给出一套可直接上手的排障方法——目标是让你对 DNS 的理解从「会用 dig」进阶到「知道每一跳发生了什么」。
一、分层架构:根 → TLD → 权威,逐级委托
DNS 的命名空间是一棵倒挂的树。每个节点把子域的管理权「委托」(delegation)给下级服务器,这是它能够横向扩展到全球规模的核心设计:
| 层级 | 负责内容 | 数量级 |
|---|---|---|
| 根服务器(.) | 只回答「.com / .cn / .org 的 NS 在哪」 | 全球 13 个 IP(任播扩展至上千实例) |
| TLD 权威(.com 等) | 只回答「example.com 的 NS 在哪」 | 由注册局运营(Verisign 等) |
| 域权威(example.com) | 回答该域内所有记录的最终答案 | 你自己或云厂商(如 DNSPod、Route 53) |
关键认知:根和 TLD 通常并不知道你的最终 IP,它们只负责「指路」。逐级委托意味着每一级只需要维护自己那一份小数据,这也解释了为什么这套 1983 年设计的协议至今仍能支撑整个互联网。
二、递归与迭代:一次完整解析的两种角色
客户端(严格说是 stub resolver,藏在 OS 里)只会做一件事:把问题抛给配置的递归解析器(如运营商的 114.114.114.114,或公共的 8.8.8.8),然后等一个最终答案。剩下的「跑腿」全由递归解析器完成:
- 客户端 → 递归解析器:「www.example.com 的 A 记录是什么?」
- 递归解析器 → 根服务器(迭代查询):根答「去问 .com 的 NS」(并给出地址)
- 递归解析器 → .com 权威:答「去问 example.com 的 NS」
- 递归解析器 → 域权威:答「www 的 A 记录是 93.184.216.34,TTL 3600」
- 递归解析器 → 客户端:返回最终答案(并顺手缓存一份)
递归(recursive)=「你必须给我最终答案」;迭代(iterative)=「你知道多少答多少,我自己接着问」。客户端对解析器是递归关系,解析器对各级权威是迭代关系。用一条命令就能看到全程:
$ dig +trace www.example.com
;; ANSWER SECTION:
www.example.com. 3600 IN A 93.184.216.34
;; Received 156 bytes from 192.33.4.12#53(g.gtld-servers.net)
;; ...逐级打印 root → .com → 权威的应答过程
另一个常被忽略的细节:非权威应答(aa=0)与权威应答(aa=1)。dig 输出中的 flags 行出现 aa 说明答案直接来自该域的权威服务器;来自递归解析器的答案多半是缓存,flags 里是 ra(recursion available)。
三、记录类型:不只是 A 记录
| 类型 | 作用 | 典型场景 / 易错点 |
|---|---|---|
| A / AAAA | 域名 → IPv4 / IPv6 地址 | 双栈站点两者都要配;AAAA 配错是「部分用户打不开」的经典原因 |
| CNAME | 别名指向另一个域名 | 不能与其他记录共存——所以「裸域 CNAME 到 www」在标准 DNS 里做不到(需 ALIAS/显式 A 记录) |
| MX | 邮件交换服务器(带优先级) | 值必须是主机名而非 IP;优先级数字越小越优先 |
| TXT | 任意文本 | SPF / DKIM / 域名所有权验证全靠它 |
| NS | 子域委托给哪组服务器 | NS 记录的值也必须是主机名 |
| SOA | 域的元信息(主 NS、序列号、刷新参数) | 负缓存 TTL 就藏在 SOA 的 minimum 字段里 |
| CAA | 允许哪些 CA 给本域签发证书 | 建议显式配置,缩小误签发面 |
四、TTL 与多级缓存:为什么改了记录不生效
解析一次要跨越大半个互联网,显然不能每次都走全流程。DNS 的答案是在每一层都缓存,用 TTL 控制有效期:
- 浏览器缓存(Chrome 里 chrome://net-internals/#dns)
- 操作系统缓存(Linux 由 nscd/systemd-resolved 管理,Windows 有自己的 DNS Client 服务)
- 本地 hosts 文件(优先级在 DNS 之前)
- 递归解析器缓存(影响面最大的一层——它服务成千上万用户)
- 权威服务器(答案的源头,无缓存概念)
$ dig www.example.com
;; ANSWER SECTION:
www.example.com. 3600 IN A 93.184.216.34
^^^^ TTL:剩余有效秒数(每查询一次递减)
这里有两个实战级结论:
其一,TTL 是「你愿意让旧数据存活多久」的承诺,各级缓存只认首次拿到答案时的值。所以标准的服务器迁移流程是:提前 24~48 小时把 TTL 降到 60 秒 → 切换 IP → 观察流量迁移完成 → 再把 TTL 调回。直接切 IP 再改记录,旧地址会在各级缓存里顽固存活原 TTL 时长。
其二,不存在 NXDOMAIN 缓存bug 之外还有「负缓存」:域名不存在(NXDOMAIN)的答案也会被递归解析器缓存一段时间,时长由权威侧 SOA 记录的 minimum(负缓存 TTL)决定。刚注册/刚加的解析不生效,先想想是不是负缓存在捣乱。
五、排障实战:把 dig 用成手术刀
1. 指定服务器对比,快速定位问题层
dig www.example.com @8.8.8.8 # 公共递归的结果
dig www.example.com @ns1.example.com # 直接问权威(绕过一切缓存)
dig www.example.com +norecurse @8.8.8.8 # 只看缓存,不发起递归查询
公共解析器有答案、权威没有 → 配置还没生效或同步延迟;权威有、公共没有 → 缓存/传播延迟,等 TTL 过期;两者都没有 → 记录本身就没配上。
2. 查 NS 链路,确认委托一致
dig example.com NS +short # 应返回注册商处登记的那组 NS
dig @a.gtld-servers.net example.com NS # 从 TLD 视角看委托给了谁
域名「换了 DNS 服务商但没在注册商处改 NS」是新手最常见的事故之一——你在新服务商配得再好,全世界问的还是旧 NS。
3. 检查答案的权威性
dig example.com A +noall +answer +comments
;; flags: qr rd ra; # ra:来自递归缓存
;; flags: qr aa rd ra; # aa:权威直答
六、DNS 安全:投毒、劫持与加密
DNS 诞生时对信任毫无设防,UDP 明文、无签名。三个主要威胁与对策:
| 威胁 | 原理 | 对策 |
|---|---|---|
| 缓存投毒 | 伪造「更快」的应答骗过递归解析器写入假记录 | 源端口随机化(已普及)、DNSSEC 签名验证 |
| 运营商劫持 | 明文 UDP 应答被中间设备替换(插广告、404 导航页) | DoT(853 端口 TLS)或 DoH(443 端口 HTTPS)加密查询 |
| 子域接管 | CNAME 指向已被释放的云资源,攻击者认领后接管内容 | 清理悬空记录;定期扫描 CNAME 指向 |
DNSSEC 用一条从根逐级下传的签名链(RRSIG → DS 记录)保证「答案没被篡改」——注意它保护的是完整性而非机密性,查询本身仍是明文;想防窃听/劫持要叠 DoH/DoT。两者互补而非替代。
七、自建场景速览:dnsmasq / BIND9 / Unbound
# dnsmasq:内网首选,DNS+DHCP 一体
address=/intranet.corp/10.0.0.10 # 内网域名直接映射
server=/corp/10.0.0.53 # corp 后缀转发给内网权威
server=223.5.5.5 # 其余走公网递归
内网 DNS 的经典模式是「转发 + 本地覆盖」:公网域名向上游转发,内网域名本地应答。规模再大一点上 BIND9(完整权威+递归)或 Unbound(纯递归、默认开启 DNSSEC 验证)。选型逻辑很简单:几十台机器用 dnsmasq,要对外提供权威解析用 BIND9/云厂商,专注内网递归质量用 Unbound。