DNSSEC是什么?数字签名如何防止DNS缓存投毒?
一个攻击者坐在某个网络节点上,盯着流经的DNS查询报文。他看到你向递归服务器发出了一个查询:“bank.example 的IP是多少?”他抢在真正的权威服务器返回之前,伪造一个响应包发给你:“bank.example 的IP是 6.6.6.6。”你的递归服务器接受了这个响应,把它写进了缓存。接下来的几个小时里,所有向你这家递归服务器查询 bank.example 的用户,都会被引导到 6.6.6.6。
这就是DNS缓存投毒的经典剧本。它的成立不需要攻击者拥有多么高超的技术,只需要他能在正确的时机发出一张伪造的“身份证”。而DNSSEC要做的事情,就是让这张伪造的身份证在验证环节被当场识破。
问题的根源:DNS的“信任”是盲目的
DNS协议设计于1983年,那个年代的互联网还是一个相对封闭的学术网络,设计者默认网络上的参与者是善意的。查询和响应走UDP,没有握手,没有身份验证。递归服务器收到一个响应包,只看两样东西:Transaction ID对不对,源端口对不对。只要对上了,它就信。
这种“先到先得”的信任模型,是缓存投毒能够成立的根基。
RFC 4033的文档里直白地描述了没有DNSSEC时的状态:一个DNS解析器向外发出查询,然后不加质疑地接受它收到的第一个响应。如果这个响应是伪造的,解析器就会一直用这个错误地址,直到缓存过期。
问题在于,如果被投毒的不是一台普通用户的电脑,而是一个ISP的递归服务器,那么所有使用这台递归服务器的用户都会受到影响。这就是“缓存投毒”的放大效应。
DNSSEC的核心:给每一条DNS记录打上数字签名
DNSSEC(Domain Name System Security Extensions)做的事情,用一句话概括就是:让DNS数据的来源和完整性可以被密码学验证。
它不加密DNS数据,DNS记录依然是明文传输的。它做的事情是在DNS记录旁边附加一个数字签名。这个签名是权威DNS服务器用只有它自己知道的私钥对记录内容进行运算后生成的。任何持有对应公钥的人,都可以验证这个签名是否匹配记录内容。
打个比方:以前的DNS响应像一张手写的便条,任何人都可以假冒笔迹。DNSSEC给这张便条加了一个只有本人才能生成的“火漆印章”。印章本身可以被任何人用公开的模具验证真伪,但伪造印章需要拥有那把私钥。
具体到DNS的报文结构里,DNSSEC引入了四种新的记录类型:
RRSIG(Resource Record Signature):数字签名本身。它随DNS响应一起返回,包含了签名值、签名有效期、签名者名称等信息。
DNSKEY(DNS Public Key):公钥。权威服务器用它来公布验证签名所需的公钥。任何人可以通过DNS查询获取这个公钥。
DS(Delegation Signer):委托签名者。存储在父区域中,用来验证子区域DNSKEY的哈希值。这是“信任链”的关键环节。
NSEC/NSEC3(Next Secure):用来证明“某个记录不存在”。没有DNSSEC时,攻击者可以伪造一个“域名不存在”的响应;有了NSEC,权威服务器可以对“不存在”本身进行签名,让解析器能够验证这个“否定回答”也是真实的。
信任链:从根区域一层层“担保”下来
只有数字签名还不够,因为攻击者可以伪造一整套东西:伪造DNSKEY,伪造RRSIG,然后自说自话地“验证通过”。
DNSSEC解决这个问题的办法是层级担保,也就是“信任链。
逻辑是这样的:ICANN的根区域密钥(KSK)是整个体系的“信任锚点”。根区域用它的私钥对.com区域的DS记录签名,意思是“我担保.com区域的公钥是可信的”。.com区域再用自己的私钥对example.com区域的DS记录签名,担保example.com的公钥可信。example.com再用自己的私钥对自身的DNS记录(比如A记录、MX记录)签名。
一个开启了DNSSEC验证的递归解析器,在收到一个响应时,会沿着这条信任链从根一路验证到目标域名。每一步都检查:父区域的签名是否有效?子区域的公钥哈希是否匹配DS记录?最终的数据签名是否与数据本身匹配?
如果任何一步对不上,解析器就判定这个响应是Bogus(伪造) 状态。根据配置,它要么返回一个错误(SERVFAIL),要么拒绝使用这个数据。攻击者伪造的响应包无论看起来多么“像真的”,都无法通过这条密码学链条的验证。
数字签名如何阻止缓存投毒?
回到攻击场景。
攻击者仍然可以抢在真正的权威服务器之前,向递归服务器发送一个伪造的响应包。Transaction ID猜对了,源端口也猜对了。但伪造包里没有有效的RRSIG签名。
没有签名,或者签名的验证失败,递归服务器就不会把这个数据当作“Secure”状态接受。对于已经启用DNSSEC验证的解析器来说,一个没有通过验证的响应,等同于无效。
这就是DNSSEC对缓存投毒的致命打击:投毒成功的前提是递归服务器“相信”伪造的数据。DNSSEC让“相信”这个动作变得有条件了——你得先证明你是真的。
RFC 4033定义了验证解析器可以判定的四种状态:Secure(有信任锚、信任链完整、签名全部验证通过)、Insecure(能证明这个区域本来就不安全,比如父区域明确声明没有DS记录)、Bogus(有安全委托但验证失败)、Indeterminate(没有信任锚,无法判断)。缓存投毒的伪造数据,在开启验证的解析器上会被标记为Bogus,被丢弃。
DNSSEC不做什么?
理解DNSSEC的边界,和理解它做什么同样重要。
DNSSEC不加密DNS查询和响应。数据依然是明文传输的,中间人可以看到你在查询什么域名,只是无法篡改响应而不被发现。想要防止窃听,需要的是DoT或DoH。
DNSSEC不保护源站服务器本身。它保护的是“域名到IP地址”这个映射关系的真实性,不保护IP地址对应的服务器是否安全。
DNSSEC不是SSL证书的替代品。Verisign的文档里有一个精辟的概括:DNSSEC确保用户被引导到正确的网站,SSL证书则验证这个网站的身份并加密通信。两者互补,缺一不可。
部署的现实:为什么你很少“感受到”它
DNSSEC的根区域在2010年完成签名。.com、.net等主要顶级域也早已支持。但普通用户很少直接感知到DNSSEC的存在,原因在于部署率。
一个域名要享受DNSSEC的保护,需要注册局支持DNSSEC、域名持有人主动开启签名、递归解析器(通常是ISP的)开启验证。这三个环节中任何一个缺失,DNSSEC的保护就不会生效。ICANN在2012年的数据是“已签名二级域名不足1%”,虽然这个比例这些年有所提升,但距离“默认开启”还有很长的路。
所以对于大多数普通用户来说,日常上网的DNS解析仍然运行在没有DNSSEC验证的状态下。DNS缓存投毒的风险在协议层面依然存在,只是攻击者是否选择你的解析器作为目标,是另一回事。
CN
EN