域名系统(DNS)就如同互联网的电话簿:它告诉计算机向哪里发送信息,从哪里检索信息。可惜的是,它也接受互联网提供给它的任何地址,而不会进行任何询问。
电子邮件服务器使用 DNS 路由邮件,这意味着它们容易受到 DNS 基础设施的安全性问题影响。2014 年 9 月,CMU 的研究人员发现,本应通过 Yahoo!、Hotmail 和 Gmail 服务器发送的电子邮件变成通过流氓邮件服务器发送。攻击者利用了域名系统(DNS)中一个已存在数十年之久的漏洞,即接受应答前不会检查凭据。
这个问题的解决方案是一项名为 DNSSEC 的协议;通过提供身份验证,该协议在 DNS 之上增加了一个信任层。在某个 DNS 解析器查找 blog.cloudflare.com时,.com 名称服务器帮助解析器验证针对 cloudflare 返回的记录,而 cloudflare 帮助验证针对 blog 返回的记录。根 DNS 名称服务器帮助验证 .com,而根服务器发布的信息通过一个彻底的安全程序(包括“根签名仪式”)进行审查。
DNSSEC 通过向现有 DNS 记录添加加密签名,确保域名系统的安全性。这些数字签名与 A、AAAA、MX、CNAME 等常见记录类型一起存储在 DNS 名称服务器中。通过检查其相关签名,您可以验证所请求的 DNS 记录是否来自其权威名称服务器,并且在途中没有被更改,不是在中间人攻击中注入的虚假记录。
为了促进签名验证,DNSSEC 添加了一些新的 DNS 记录类型:
RRSIG - 包含加密签名
DNSKEY - 包含公共签名密钥
DS - 包含 DNSKEY 记录的哈希
NSEC和NSEC3 - 用于明确否认 DNS 记录的存在
CDNSKEY和CDS - 用于请求对父区域中的 DS 记录进行更新的子区域。
RRSIG、DNSKEY 和 DS 记录之间的交互,以及它们如何在 DNS 之上添加信任层,就是我们在本文中要讨论的内容。
DNSSEC 中的每个区域都有一个区域签名密钥对(ZSK):密钥的专用部分对区域中的每个 RRset 进行数字签名,而公共部分则验证签名。为了启用 DNSSEC,区域操作员使用专用 ZSK 为每个 RRset 创建数字签名,并将其作为 RRSIG 记录存储在名称服务器中。这就像是说:“这些是我的 DNS 记录,它们来自我的服务器,看起来应该像这样。”
但是,除非 DNS 解析器拥有验证签名的方法,否则这些 RRSIG 记录起不到作用。区域操作员还需要将公用 ZSK 添加到 DNSKEY 记录中的名称服务器,方能使其可用。
当 DNSSEC 解析器请求特定的记录类型(例如 AAAA)时,名称服务器还会返回相应的 RRSIG。然后,解析器可以从名称服务器中提取包含公用 ZSK 的 DNSKEY 记录。RRset、RRSIG 和公共 ZSK 将一同用于验证响应。
如果我们信任 DNSKEY 记录中的区域签名密钥,则可以信任该区域中的所有记录。但是,如果区域签名密钥被泄露怎么办?我们需要一种方法来验证公开 ZSK。
除了区域签名密钥之外,DNSSEC 名称服务器还具有密钥签名密钥(KSK)。KSK 验证 DNSKEY 记录的方式与上一节中描述的、我们的 ZSK 保护其余 RRset 的方式完全相同:它签署公共 ZSK(存储在 DNSKEY 记录中),从而为 DNSKEY 创建 RRSIG。
就像公用 ZSK 一样,名称服务器将公用 KSK 发布在另一个 DNSKEY 记录中,而这就给我们提供了上面显示的 DNSKEY RRset。公共 KSK 和公共 ZSK 均由私有 KSK 签名。然后,解析器就可以使用公共 KSK 来验证公共 ZSK。
现在,解析器的验证如下所示:
请求所需的 RRset,系统还将返回相应的 RRSIG 记录。
请求包含公用 ZSK 和公用 KSK 的 DNSKEY 记录,系统还将返回 DNSKEY RRset 的 RRSIG。
用公共 ZSK 验证所请求 RRset 的 RRSIG。
用公共 KSK 验证 DNSKEY RRset 的 RRSIG。
当然,DNSKEY RRset 和相应的 RRSIG 记录可以缓存,以防 DNS 名称服务器被不必要的请求不断轰炸。
为什么我们使用单独的区域签名密钥和密钥签名密钥?如我们将在下一节中讨论的那样,要替换掉旧的或受损的 KSK 很难。另一方面,更改 ZSK 就容易得多。这使我们可以使用较小的 ZSK,而不会损害服务器的安全性,因此最大程度地减少了每次响应时服务器必须发送的数据量。
现在,我们在区域内建立了信任关系,但是 DNS 是一个分层系统,各区域很少独立运行。而密钥签名密钥是由其自身签名的,不会提供额外的信任基础,因此使情况更加复杂。我们需要一种方法将我们区域中的信任与其父区域联系起来。
DNSSEC 引入了委派签名者(DS)记录,以允许将信任从父区域转移到子区域。区域操作员对包含公共 KSK 的 DNSKEY 记录进行哈希处理,并将其提供给父区域以作为 DS 记录发布。
每次将解析器引用到子区域时,父区域也会提供 DS 记录。此 DS 记录是解析器获知子区域启用 DNSSEC 的方式。为了检查子区域的公共 KSK 的有效性,解析器对其进行哈希处理并将其与父区域的 DS 记录进行比较。如果两者匹配,则解析器可以假定公共 KSK 未被篡改,这意味着它可以信任子区域中的所有记录。这就是在 DNSSEC 中建立信任链的方式。
请注意,KSK 的任何变更都需要更改父区域的 DS 记录。更改 DS 记录是一个多步骤的过程,如果执行不正确,最终可能会破坏该区域。首先,父级需要添加新的 DS 记录,然后需要等到原始 DS 记录的 TTL 过期后将其删除。这就是为什么换掉区域签名密钥比密钥签名密钥要容易得多。
如果您向 DNS 询问不存在的域的 IP 地址,它将返回一个空答案 - 无法明确答复“抱歉,您请求的区域不存在”。如果您想对响应进行身份验证,这就是一个 问题,因为没有可签名的消息。DNSSEC 通过添加 NSEC 和 NSEC3 记录类型来解决此问题。它们都允许对否认存在的答复进行身份验证。
NSEC 会返回“下一个安全”的记录。例如,假设有一个为 api、blog 和 www 定义 AAAA 记录的名称服务器。如果您请求 store 的记录,它将返回一个包含 www 的 NSEC 记录,表示当记录按字母顺序排序时,strore 和 www 之间没有 AAAA 记录。这明确地告诉您 store 不存在。并且,由于 NSEC 记录经过签名,您可以像验证任何 RRset 一样验证其相应的 RRSIG。
但是,使用这种解决方案,人们无需知道他们要寻找的记录,就可以浏览区域并收集每条记录。如果区域管理员希望该区域的内容为隐私内容,则这可能造成潜在的安全威胁。您可以在 DNSSEC:复杂性和注意事项了解有关此问题的更多信息,并在 DNSSEC Done Right 了解 Cloudflare 的独特解决方案。
现在,在区域内建立信任并将其连接到父区域的方法已经有了,但是我们如何信任 DS 记录呢?DS 记录就像其他任何 RRset 一样签署,这意味着它在父级中具有相应的 RRSIG。整个验证过程不断重复,直到获得父级的公共 KSK。为了验证父级的公共 KSK,我们需要转到父级的 DS 记录,以此类推,沿着信任链上行。
但是,当我们最终到达根 DNS 区域时,又有一个问题:没有父 DS 记录可用于验证。在这里,我们可以看到全球互联网非常人性的一面:
在“根区域签名仪式”上,来自世界各地的特定几人以公开且经严格审核的方式签署根 DNSKEY RRset。这次仪式会产生一个 RRSIG 记录,该记录可用于验证根名称服务器的公共 KSK 和 ZSK。我们不会由于父级的 DS 记录而信任公共 KSK,而是因为信任访问私有 KSK 所涉的安全性过程而假定其有效。
在父区域和子区域之间建立信任的能力是 DNSSEC 不可或缺的一部分。如果信任链中的任何部分被破坏,我们就不能信任我们所请求的记录,因为中间人可能会更改记录并将我们引导到任何 IP 地址。
与 HTTPS 相似,DNSSEC 在原本不安全的协议之上启用经过身份验证的答案,因此增加了一个安全层。HTTPS 会对流量进行加密,以便网络上的任何人都无法窥探您的 Internet 活动,而 DNSSEC 只是对响应进行签名,从而检测伪造响应。DNSSEC 提供了解决实际问题的解决方案,并且无需结合加密。
Cloudflare 的目标是让启用 DNSSEC 的过程 尽可能简单轻松。所有 Cloudflare 客户都可以通过如下方式为其 Web 资产添加 DNSSEC: 打开一个开关以启用 DNSSEC,并将一个 DS 记录(由我们自动生成)上传到他们的注册商:进一步了解如何获取 DNSSEC。
我们还发布了一份互联网草案(Internet Draft),其中概述了注册管理机构和注册商代表我们的客户上传 DS 记录的一种自动化方法。这将使 Cloudflare 能够为我们的整个社区自动启用 DNSSEC。请关注最新消息。