DNS 的本质

DNS 本质上是一个分布式数据库,将人类可读的域名映射为机器可读的 IP 地址。它的设计目标是:去中心化、高可用、高性能。

完整解析链路

当你在浏览器输入 www.google.com 时,DNS 解析经历以下步骤:

浏览器缓存 → OS 缓存 → hosts 文件 → 本地 DNS 服务器 → 根DNS → 顶级域DNS → 权威DNS

第一步:本地缓存查询

1. 浏览器 DNS 缓存(Chrome: chrome://net-internals/#dns)
2. 操作系统缓存(Linux: /etc/nsswitch.conf 控制顺序)
3. /etc/hosts 文件
4. 本地 DNS 服务器缓存

第二步:递归查询

本地 DNS 服务器(如 223.5.5.5)代替客户端完成整个解析过程:

客户端 → 本地DNS: "www.google.com 的 IP 是什么?"
本地DNS → 根DNS (.): "请问 .com 在哪?"
根DNS → 本地DNS: "去找 .com 顶级域服务器"
本地DNS → .com TLD: "www.google.com 在哪?"
.com TLD → 本地DNS: "去找 google.com 权威DNS"
本地DNS → google.com 权威: "www.google.com 在哪?"
google.com 权威 → 本地DNS: "142.250.80.46"
本地DNS → 客户端: "142.250.80.46(缓存TTL=300秒)"

第三步:迭代查询(权威 DNS 之间)

根 DNS、TLD DNS、权威 DNS 之间使用迭代查询——每个 DNS 服务器只告诉下一步该找谁,不代替你完成全部查询。

DNS 记录类型

类型用途示例
A域名 → IPv4example.com → 93.184.216.34
AAAA域名 → IPv6example.com → 2606:2800:220:1::248
CNAME域名别名www.example.com → example.com
MX邮件服务器example.com → mail.example.com
TXT文本记录SPF、DKIM、域名验证
NS域名服务器example.com → ns1.example.com
SOA起始授权包含序列号、刷新间隔等
PTR反向解析34.216.184.93 → example.com

DNS 缓存与 TTL

TTL(Time To Live)

每条 DNS 记录都有 TTL 值(秒),表示缓存有效期:

# 查询 TTL
dig example.com +ttlunits
# 或
dig example.com | grep -A1 "ANSWER SECTION"

# 输出示例
# example.com.    300    IN    A    93.184.216.34
#                        ^^^^ TTL=300秒

缓存层级

浏览器缓存(几分钟)
OS 缓存(TTL 值)
路由器缓存(几小时)
ISP DNS 缓存(TTL 值)
公共 DNS 缓存(TTL 值)

缓存清除

# Linux
sudo systemd-resolve --flush-caches
# 或
sudo resolvectl flush-caches

# macOS
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

# Windows
ipconfig /flushdns

DNS 劫持与防御

常见劫持方式

  1. DNS 缓存投毒:向 DNS 服务器注入虚假记录
  2. 本地 hosts 篡改:修改 hosts 文件
  3. ISP 劫持:运营商透明代理 DNS 请求
  4. 中间人攻击:拦截 DNS 查询并返回虚假响应

检测方法

# 对比不同 DNS 服务器的结果
dig @8.8.8.8 example.com
dig @223.5.5.5 example.com
dig @1.1.1.1 example.com

# 如果结果不一致,可能存在劫持

防御手段

1. DoH(DNS over HTTPS)

# 使用 DoH 查询
curl -H 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=example.com&type=A'

DoH 的优势:

  • 流量加密,无法被中间人窃听
  • 隐藏在 HTTPS 流量中,难以被识别和阻断
  • 使用标准 443 端口,兼容性好

2. DoT(DNS over TLS)

# 使用 DoT(端口 853)
kdig -d example.com @1.1.1.1 +tls

DoT 的特点:

  • 专用端口 853,容易被识别和封锁
  • 加密效果与 DoH 相同
  • 适合内网环境

3. 本地 DNS 加密

# 使用 dnscrypt-proxy
apt install dnscrypt-proxy
# 配置 /etc/dnscrypt-proxy/dnscrypt-proxy.toml

DNS 性能优化

本地 DNS 缓存服务

# 使用 systemd-resolved
sudo systemctl enable systemd-resolved
sudo systemctl start systemd-resolved

# 配置 /etc/systemd/resolved.conf
[Resolve]
DNS=223.5.5.5 119.29.29.29
FallbackDNS=8.8.8.8
Cache=yes
DNSStubListener=yes

DNS 预解析

<!-- 在 HTML 中预解析常用域名 -->
<link rel="dns-prefetch" href="//www.google.com">
<link rel="preconnect" href="https://www.google.com">

缓存大小优化

# 查看 systemd-resolved 缓存状态
resolvectl statistics

# 查看缓存命中率
resolvectl query example.com

面试常见问题

  1. DNS 使用 TCP 还是 UDP?

    • 主要使用 UDP(响应快、开销小)
    • 区域传送(AXFR)使用 TCP
    • 响应超过 512 字节时切换到 TCP(EDNS0 可扩展到 4096)
  2. DNS 为什么不用 TCP?

    • TCP 需要三次握手,延迟高
    • UDP 简单快速,适合短查询
    • DNS 查询通常很小,不需要 TCP 的可靠性保证
  3. 什么是 DNS 放大攻击?

    • 攻击者伪造源 IP 向开放 DNS 服务器发送查询
    • DNS 服务器将大量响应发送到受害者 IP
    • 利用 DNS 响应比请求大的特点进行放大
  4. 什么是 EDNS0?

    • DNS 扩展机制,支持更大的 UDP 包(最大 4096 字节)
    • 支持 DNSSEC、DNS COOKIE 等新特性

实战:搭建本地加密 DNS

# 安装 dnscrypt-proxy
apt install dnscrypt-proxy

# 配置 /etc/dnscrypt-proxy/dnscrypt-proxy.toml
listen_addresses = ['127.0.0.1:53']
server_names = ['alidns', '腾讯DNS']
max_clients = 250
ipv4_servers = true
dnssec = true