什么是 NSEC 和 NSEC3?

域名系统既要能证明一个名字存在,也要能证明它不存在。前一件事靠签名,后一件事麻烦得多——你没法对"没有"这件事签名。NSEC 和 NSEC3 就是为此设计的两种记录,后者还改掉了前者泄露整个区内容的毛病。

第 71 期讲过 DNSSEC 的作用:给解析结果加签名,让解析器能验证数据有没有被改动。但域名系统的回答不止"存在"一种。用户打错一个字,解析器会收到一条"这个名字不存在"的应答,这条否定应答同样需要能被验证——否则有人只要伪造成"不存在",就能把访问引向别处。

证明"不存在"没有直接的办法。签名只能盖在已有的记录上,而"没有记录"不是一个可以拿来签的对象。NSEC 的思路是间接证明:把区里所有名字按规范顺序排成一列,每个名字用一条 NSEC 记录指向排在它后面的那个名字,同时列出自己名下有哪些记录类型。解析器问一个不存在的名字时,服务器返回一条 NSEC,说明这个查询名落在哪两个名字之间,中间是空的。

这个办法有效,但代价写在标准里:只要反复查询不存在的名字,就能把整个区的内容一条条走一遍。RFC 5155 在开篇就直接承认了这一点,还把后果写了出来——收集邮箱地址做垃圾邮件、批量查询域名信息。

NSEC3 是为此做的改版。名字不再直接出现,owner name 换成原名的密码学哈希,列里排的是哈希值,照样能证明两个哈希之间存在空档。哈希不可逆,所以走一遍也读不出原始名字——除非你已经猜到某个名字,算一下比对就能确认它在不在。

剩下的设计是哈希算法、盐值和迭代次数。迭代听起来越多越安全,标准却后来把这条路封了:RFC 9276 在更新 NSEC3 用法时写明,如果一定要用 NSEC3,迭代次数必须取 0。理由是攻击者多数在本地做穷举,加迭代挡不住他,反而让全球解析器多算一遍。盐也只在在线轮换时才有意义。这份标准的首选其实是干脆改用 NSEC。

按老标准配出来的区里,仍能看到不为零的迭代次数。历史遗留和兼容性摆在那里,标准没有强制要求升级。