DNSSEC是为了解决传统 DNS 系统中的各种不安全性,由IETF制定的一套配合现有 DNS 系统的安全扩展系统,目标在于解决各种 DNS 缓存投毒/生日攻击/DNS 劫持等问题,从源头上保证 DNS 数据的正确性和完整性。
1、 传统 DNS 系统的弱点
DNS 系统的通讯采用的是 UDP 协议,这种不面向连接的协议适用于快速大量的传输小数据,这也是 DNS 系统采用 UDP 的原因之一。当然 DNS 也可以使用 TCP 协议,不过那是默认用于 NameServer(名称服务器,以下简称 NS) 之间的 Zone Transfer 的,普通的 DNS 解析器并不会使用 TCP 协议向 NS 发起请求。作为整个互联网最重要的基础协议之一,DNS 其实是一个非常脆弱的系统,它面对的主要可能问题有 DNS 缓存投毒/DNS 劫持/生日攻击/DNS 数据泄露等,前两者对普通用户的危害是非常恐怖的,它完全可以把一个银行域名指向黑客的服务器。
这些弱点的根本原因是 UDP 协议是不面向连接的协议,没人可以保证端对端的传输是安全的,而 DNS 协议本身在建立初期又实在太纯洁了,根本没有考虑各种后来可能出现的攻击,DNS 协议本身对数据的验证是非常脆弱的。基本的验证只有一个 Transaction ID,这种东西用生日攻击就可以猜测出来,成功率根本不用你操心; 如果是在局域网,直接在网关监听甚至可以捕获 Transaction ID,捕获后直接用于伪造 DNS 解析数据返回给客户端就行了。
首先科普一下——公钥/私钥加密的基本原理
公钥私钥加密体系是基于这样两个前提:
• 你产生一对公钥/私钥之后,如果你仅仅持有公钥或者私钥中的一个密钥,无论你配合明文,密文还是别的数据,都不可能推导出另一个密钥。
• 无论公钥还是私钥都可以用于加密数据,但加密后的数据要想解密,就必须由另一半完成。
所以公钥/私钥体系的应用主要有两种:
• 加密数据
例如现在 A 要给 B 发送数据,他就先用 B 的公钥加密消息 (因为公钥是公开的,人人都可以有的),然后发送给 B,即使是广播发送也无所谓,因为只有 B 才持有 B 的私钥,所以只有 B 才能解密这个消息。
• 验证签名
例如我要告诉你我是总统,我就用总统的私钥加密一个签名然后给你看,因为你们都有总统的公钥,所以都可以解密这个签名看看是不是我。其实我用任何私钥加密数据,你们都可以用总统的公钥解密,但是只有用总统的私钥加密的数据,解密后的签名才是有意义的,否则可能只是一堆乱码,你们一看: 我靠,神经病啊,打死!
2、DNSSEC 中公钥私钥的应用
在传统的 DNS 系统中,各种 A 记录、CNAME 记录、MX 记录、统称为 RR(Resource Record ),这些
RR 一旦 DNS 服务器发送给解析器,解析器就无条件接受,也不管数据对不对 (可能被缓存投毒了),甚至到底是不是服务器发回来的 (可能被 DNS 劫持了),其实也不是解析器想这样无条件接受,而是解析器除了接受也没办法验证啊。
为了解决这个问题,DNSSEC 增加了 RRSIG(Resource Record Signature,RR 签名), DNSKEY(Domain’s Public Key,域名公钥),DS(DiaoSiDelegation Signer ,上级授权签名) 这三种记录,同时从根域名到顶级域名服务器都各自增加了一套公钥/私钥。我们用一个例子来解释 DNSSEC 如何利用这几个记录。
DNSSEC 中 RRSIG 的作用
DNSSEC 中 DS 和 DNSKEY 记录的作用
0. 我们 (一台 DNS 服务器) 现在要查询 paypal.com 的 DNS 记录,paypal.com 的名称服务器 (以下简称 PP_NS) 返回了对应的 A 记录 IP 数据 (RR,Resource Record,A 记录、MX 记录这些记录都统称为AR) 和一长串字符 (RRSIG),还有对应的 DNSKEY 和 DS 记录,那我怎么知道这个 IP 是真的 A 记录,而不是被修改了的呢?
1. 先看看 RRSIG 是什么产生的,PP_NS 对 RR 做哈希,然后用自己的私钥对这个哈希进行加密,就产生了 RRISG。
2. 而查询到的 DNSKEY,就是 PP_NS 的公钥,我们用这个公钥对这个 RRSIG 解密就得到了 RR 的哈希值,然后我们自己再对 RR 做一次哈希,通过判断两个哈希是否一样就可以知道数据是否正确了。
3. 你也许会想,我 tm 怎么知道这些 DNSKEY 真的就是 PP_NS 发回来的,说不定连你整套公钥/密钥都是黑客伪造的呢? 这里就用到了 DS。
4. DS 的产生是这样的,PP_NS 把自己的公钥交给给上级的 NS,也就是.com 顶级域名的名称服务器(以下简称 COM_NS) 那里,用 COM_NS 的私钥对 PP_NS 的公钥加密,就得到了 DS。
5. 如果你不相信 PP_NS 的公钥,你可以去 COM_NS 那里查 COM_NS 的公钥,然后对 PP_NS 的 DS 记录解密,如果解密结果和 DNSKEY 对上了,就说明 DNSKEY 是对的.COM_NS 这个级别的数据你总该相信了吧。
6. 如果连 COM_NS 的公钥都不相信,没关系,COM_NS 自己也有 DS 记录,这个 DS 是由跟服务器 (ROOT_NS) 用自己的私钥加密产生的,你再用 ROOT_NS 的公钥,做一样的解密验证就可以了。
7. 如果你不相信 ROOT_NS 的公钥,那你他妈别玩了! 这个公钥是你自己告诉 DNS 服务器的,根本不用你去查询。
ROOT_NS 的公钥,就是一般的解析器和 DNS 服务器里设定的 Trust Anchor,因为这个数据最终是你自己设定的,所以整个验证链最后的安全阀其实在你手里,但你有责任自己去维护这个 Trust Anchor 的正确性。如果 ROOT_NS 的公钥变动,就必须跟着修改,否则顶层一级的解密校验就会失败。这个 Trust Anchor 虽然是你自己输入,但并不是你随意产生的,这个实际上已经由 IANA 公布,可以在这里找到最新的 Root Trust Anchor。
以上就是 DNSSEC 的大致原理,简单来说就是从已知的根域开始,一级级对下级签名,然后查询者可以通过这个签名链的逆过程验证每一个数据是否真实。