事情发生在上周四晚上十点。一个做跨境电商的客户给我发消息,说自己的官网突然打不开了,浏览器报的是「您的连接不是私密连接」,点开证书详情一看,颁发机构变成了一个自己从没见过的名字。他第一反应是服务器被人黑了,毕竟 Nginx 上挂着后台管理入口。我让他先把服务器上的进程、计划任务、web 目录全查一遍,结果干干净净,机器连一次异常登录记录都没有。
第二次排查我把重点挪到了域名解析上。在一台干净的机器上直接 dig 那个域名,拿到的 A 记录指向一台完全陌生的机器,IP 归属地显示在境外的一个廉价机房,服务器上的站点内容是一个模仿原站的登录页,专门收集后台账号密码。这不是服务器被入侵,是 DNS 解析结果被换了也就是俗称的域名劫持。客户后来回忆,前一天下午确实收到过注册商发来的「域名信息修改验证」邮件,他点了链接。
事情查到这里,其实还只是浮出水面。DNS 本身是一套明文协议,递归解析器和权威服务器之间的查询、应答全都是可读可改的,任何能在这条链路上动手脚的人,都能给你返回一条「看起来完全正常」的假记录。要真正压住这条路,靠的是 DNSSEC。
如果你手上有自己的域名、正在自己管 DNS 解析,这台可以先看看:HostsMan(hosts 与 DNS 解析编辑工具)(系统其它,V4.0.88.0电脑版)能直接改本机 hosts 指向、批量切换内网解析,排查「域名到底解析到哪台机器」这类问题时比临时改配置方便得多。DNSSEC 配完之后如果要验证本地解析是否真的走了带签名的链路,用它切到权威服务器上观察结果,比反复改 resolv.conf 省事。
下面是我这次动手的完整过程,包括我怎么配的、在哪儿翻的车,以及做完之后该怎么盯住它。算是给同样想给自己域名加一层防线的朋友留一份能照着走的记录。

一、DNSSEC 保护的是什么,不保护什么
先把边界划清楚,不然容易花大力气做错事。DNSSEC 保护的是「解析结果的完整性和来源可信度」,也就是让递归解析器能够验证一条应答确实来自你指定的域名、并且在有效期内没有被篡改。它不加密内容DNS 查询依然是可以被中间人看到的;它也不保护你的 Web 服务,攻击者如果能拿到你的服务器权限,DNSSEC 一点忙都帮不上;它更不解决注册商账号被钓鱼这件事,客户那次点击验证邮件链接,DNSSEC 同样拦不住。
DNSSEC 真正拦的是这一类:有人伪造了一条 A 记录塞给递归服务器,或者利用缓存投毒让解析器记住一条假记录。因为你的区域文件带签名,伪造的记录没有合法签名,递归解析器校验不过就会直接丢弃,返回 SERVFAIL 而不是那条假地址。换句话说,攻击者拿不到「骗过浏览器」的机会,网站会变成打不开,而不是变成钓鱼站。

二、四个绕不开的名词:DNSKEY、RRSIG、DS、NSEC
配之前先把这几个东西对上号,不然配置过程看得人云里雾里。DNSKEY 是你域名的公钥,存放在权威 DNS 的区域文件里,同时私钥留在自己手上,用来给数据签名。RRSIG 是每条记录对应的签名值,是 dnssec-signzone 自动生成的一堆 RR 类型记录,签名里带了起始时间和失效时间。DS 记录是「父区对子区的信任凭证」,由子区自己生成,交到注册商或者父级 DNS 那边去,作用是把子区的 DNSKEY 哈希锚定到父区。NSEC(或者 NSEC3)则是用来证明「某个域名确实不存在」,防止有人伪造一个不存在的域名解析出来。
整套信任链是从根区一路往下的:根区的信任锚 → 顶级域 → 二级域 → 你的子域。每一层的父区都通过 DS 记录认证下一层的公钥。所以 DNSSEC 能不能生效,不取决于你服务器上签名做得多漂亮,而取决于这条链上有没有断点。绝大多数「配了 DNSSEC 之后全站 SERVFAIL」的事故,断点都在 DS 记录这一环。

三、动手:BIND9 上从零签一遍
我的环境是两台权威 DNS(coredns 前面挡了一层 nginx 做负载),跑在 Debian 12 的容器里,主从同步用 BIND9 的 notify,区域文件放在本地目录。流程大致是下面这几步。
第一步生成两把密钥。KSK 是密钥签名密钥,负责签署 DNSKEY 集合本身,一般有效期设得长,两到五年;ZSK 是区域签名密钥,负责签具体的解析记录,我设成 60 天。ZSK 短是有原因的:泄露之后要能轮换,而轮换一次密钥就要重新签全区域的记录,DS 记录只有 KSK 一条,轮换起来影响面小得多。
第二步用 dnssec-signzone 签区域文件,输出带 RRSIG 的 .signed 版本,再让 named 加载这个签名后的文件。这里有个细节别忽略:签名前后的记录 TTL 必须一致,否则递归解析器拿到的 RRSIG 有效期和实际缓存时间对不上,会被判为签名失效而返回 SERVFAIL。这个坑我第一次配就踩了,区域文件里原来写的是 300 秒,签完的版本里是 3600 秒,结果全网解析直接瘫了半小时。
第三步把 DS 记录提交上去。我的域名注册商后台有 DNSSEC 的填单,把 KSK 的公钥哈希、算法类型、摘要类型填进去,提交后大概几十分钟到一天生效。第四步是验证,这一步千万别省。dnssec-failed.org 这个域名专门用来做反向验证,只要它解析失败,就说明你的链是通的;而 dig 你自己的域名加上 +dnssec 参数,看到 rrsig 记录出现、status 是 NOERROR,才算真正做完。
四、三个让我抓狂的翻车点
第一个翻车点:DS 记录填错,父区认为子区无签名。 提交 DS 的时候我把 KSK 的公钥哈希复制错了几个字符,注册商那边倒是给了「提交成功」的提示,但父区里那条 DS 记录指向的公钥和我实际用的 KSK 对不上。结果是权威服务器正常返回签名记录,递归解析器却认为签名无效,全网 SERVFAIL,而且越换解析器越完蛋。查了整整一个下午,最后是把 DS 重新提交、再等父区同步才恢复。教训是:提交完一定要用 dig +dnssec 验证父区的 DS 是不是和自己的 DNSKEY 对得上,别信注册商后台那句「已生效」。
第二个翻车点:签名过期没人管。 ZSK 我设了 60 天,签名有效期 30 天,也就是必须每 30 天重新签一次。我最初写了个手工脚本,后来忘了,某天客户说网站打不开不是攻击,就是签名悄悄过期了,而且是全站全量过期。现在我改成每天凌晨自动重新签,用 dnssec-checkzone 校验,签完把新区域推给从服务器,并且单独监控每条 RRSIG 的到期时间,剩余有效期少于 7 天就告警。这一步是 DNSSEC 运维里最容易被忽略、也最要命的一环。
第三个翻车点:密钥轮换顺序搞反,直接把自己锁在门外。 轮换 ZSK 的正确顺序是:先发布新 ZSK → 等旧 ZSK 的签名过期时间过去 → 再删旧 ZSK → 最后把区域里的 DNSKEY 集合重新签一遍。我第一次是直接删了旧 ZSK,结果解析器要验证的那条 RRSIG 还是旧密钥签的,找不到对应公钥,链直接断。因为我还有一台从服务器能读到旧区域文件,花了十几分钟回滚;换成只有一台机器的环境,这一下就是全站长时间不可用。所以轮换脚本我写成了分步的,每一步之间强制等待,绝不合并执行。

五、DNSSEC 配好之后还得盯这几件事
签名上线只是开始,后面要长期盯:一是签名剩余有效期,这是最容易翻车的地方;二是权威 DNS 的可用性,配了 DNSSEC 之后解析器多了一次验签往返,权威机的负载和延迟都会略微上升,DNSKEY 记录多的域名尤其明显;三是注册商的账号安全,DNSSEC 挡不住钓鱼,转移密码和恢复邮箱的二次验证该开还是要开;四是域名的 NS 记录,如果中途换过 NS 服务器,DS 记录要跟着在父区重新提交过一次,不然链是断的。
再补充两点常见误区。一是 DNSSEC 和 DoH/DoT 不是一回事,前者是给记录签名、防篡改,后者是加密你和解析器之间的传输通道,两者互补,很多场景下应该一起上。二是 NSEC 和 NSEC3 的选择:NSEC3 把名字哈希化,防止有人通过枚举 DNS 推断你有哪些子域名,代价是配置更复杂、CPU 开销更大,个人站点用 NSEC 足够,暴露在外的商业站点再考虑 NSEC3。
最后回到客户那边。DNSSEC 上线、DS 委派生效、签名轮换监控挂上之后,我给他做了一次模拟攻击测试:把本地解析强行指到他那台假服务器,浏览器直接报连接不安全而不是跳到假页面,因为真服务器上的证书和域名都对不上,假服务器拿不到合法证书;即便攻击者拿到了你域名的证书,DNS 记录被篡改之后签名校验失败,解析器根本不会把那条记录交给浏览器。防护做完了,但真正的根因还是那次点击了注册商的验证邮件链接所以账号安全那部分我一直没放松。
如果你也想给自己的域名加一层,这篇里写的顺序可以直接照抄:先签区域、再提交 DS、然后用 dnssec-failed.org 验证、最后把自动重签和到期告警补上。三步之内能跑通,但后面那些日常维护的细节,才是决定它半年后还灵不灵的关键。


喜欢
顶
难过
囧
围观
无聊



