WWDC22 - DNS安全:DNSSEC

4,040 阅读12分钟

我正在参加「掘金·启航计划」

引言

作为客户端开发者,DNS的相关的领域知识,一般都不太关注,然而随着你App用户增长的一定量级,而你又是那个cover整个App的人,你就一定会开始注意它,它自身的安全性问题常常会引发一些问题。

另外,作为客户端开发者,从你发起请求那一刻到页面渲染出来所经历的过程,你最好都清楚掌握。

什么是DNS

DNS 即域名系统,全称是 Domain Name System。当我们访问一个 URL 地址时,浏览器要向这个 URL 的主机名对应的服务器发送请求,就得知道服务器的 IP,对于浏览器来说,DNS 的作用就是将主机名转换成 IP 地址。也就是,DNS 是一个应用层协议,使用的是53端口,我们发送一个请求,其中包含我们要查询的主机名,它就会给我们返回这个主机名对应的 IP。

其次,DNS 是一个分布式数据库,整个 DNS 系统由分散在世界各地的很多台 DNS 服务器组成,每台 DNS 服务器上都保存了一些数据,这些数据可以让我们最终查到主机名对应的 IP。

所以 DNS 的查询过程,说白了,就是去向这些 DNS 服务器询问,你知道这个主机名的 IP 是多少吗,不知道?那你知道去哪台 DNS 服务器上可以查到吗?直到查到我想要的 IP 为止。

DNS的层次结构

DNS 服务器有 3 种类型:根 DNS 服务器、顶级域(Top-Level Domain, TLD)DNS 服务器和权威 DNS 服务器。它们的层次结构我们可以看一张session中的图

  • 根DNS服务器

    •   以www.baidu.com为例,它的完整写法应该:www.baidu.com``.,最后的 . 就是根域名。根DNS服务器的作用就是管理它的下一级-----顶级域DNS服务器。
    •   通过询问跟DNS服务器,我们可以知道一个主机名对应的顶级域DNS服务器的IP是多少,从而继续向顶级域DNS服务器发起查询请求。
  • 顶级域DNS服务器

    •   除了上面提到的www.baidu.com中的 com 是顶级域名,常见的顶级域名还有 cnorgedu 等。顶级域 DNS 服务器,也就是 TLD,提供了它的下一级,也就是权威 DNS 服务器的 IP 地址。
  • 权威DNS服务器

    •   权威 DNS 服务器返回主机 - IP 的最终映射

还有另一类重要的DNS服务器,称为本地DNS服务器(local DNS server)。严格说来,一个本地DNS服务器并不属于该DNS的层次结构,但它对DNS层次结构是至关重要的。本地DNS服务器通常与主机相隔不超过几台路由器。当主机发岀 DNS请求时,该请求被发往本地DNS服务器,它起着代理的作用,并将该请求转发到DNS服务器层次结构中,下面的流程图,完整展示了DNS服务器解析IP的全过程,如下图所示:

流程图.jpg

DNS安全问题

互联网协议,如 TCP 、TLS 和 QUIC ,依赖于 IP 地址,因此一切都是从 DNS 开始。今天 TLS 被广泛用于保护互联网通信安全,但作为基础层的 DNS 存在一些安全问题。DNS 有史以来并不安全,它是在 1983 年设计的,当时几乎没有什么安全考虑,从那以后的几年里,已经产生了很多 DNS 攻击。

DNS 缓存中毒

攻击者利用 DNS 解析的缺陷,使他们缓存不正确的 IP 地址,导致客户端连接到恶意主机。这揭示了 DNS 的一个漏洞:它没有身份验证。如今传统的 DNS 客户端无法验证应答者,因此很容易被欺骗。

主要的操作步骤有:

  1. 攻击机器向 DNS 转发器发送大量查询报文

  2. DNS 转发器向 DNS 域名服务器解析查询域名

  3. 攻击机器伪装成 DNS 域名解析服务器,向转发器回复伪造的响应报文,并实现投毒

DNS嗅探

网络流量是透明的,默认情况下 DNS 的报文是没有加密的,因此在传输的过程中,攻击者可以监听客户端和 DNS 解析服务器之间的 DNS 流量,收集客户端的历史记录。对于用户隐私来说,这是一个严重的问题。而让这种攻击成为可能的原因是 DNS 流量最初是未加密的。

为了成为安全起点,在此之上构建其他的协议,DNS 需要经过身份验证和加密。

  • 当我们使用 DNSSEC 对 DNS 响应进行签名时,它提供了身份验证

  • 当我使用 TLS 和 HTTPS 加密 DNS 解析结果时,它可以确保隐私

TLS我们在后面一篇分享中再着重讲,接下来我们先看DNSSEC。

DNSSEC

DNSSEC是DNS安全扩展,主要思想是通过在DNS记录中添加加密签名,从而为DNS解析流程提供来源可认证和数据完整性的保障。

iOS16 和 macOS Ventura 现在支持客户端 DNSSEC 验证。DNSSEC 通过在响应中附加签名来保护数据完整性。如果响应被攻击者篡改了,那么篡改后的数据签名将与原始数据不匹配。这种情况下,客户端可以检测到响应已经被篡改然后将其丢弃。

DNSSEC 还通过使用特殊类型的 DNS 记录(例如 NSEC 记录)来断言区域中记录是否存在。NSEC 记录按字母顺序安全地告诉您下一个记录名称是什么。只有它列出的名称才是存在的,任何未列出的名称都是不存在的。

例如,现在这里有三个 NSEC 记录。记录集合中显示区域 org 只有三个记录名称,A.org、C.org 和 E.org。如果有攻击者说 A.org 不存在,客户端可以检测到这种攻击。确定 A.org 的确存在,因为它列在第一个 NSEC 记录中。同样的,如果攻击者说 D.org 存在,客户端也可以检测到,因为根据第二个 NSEC 记录,D.org 位于 C.org 和 E.org 之间,但是这两个名称之间并不存在 D.org。

DNSSEC 通过建立信任链来验证记录,举个例子:设备想要解析 www.example.org 并启用 DNSSEC 验证,过程如下:

  1. 发送询问 IP 地址、签名和密钥的查询,通过响应可以建立从 IP 地址到密钥 1 的信任关系

  2. 客户端向父区域 org 发送查询,询问可用于验证密钥 1 的记录,建立从密钥 1 到密钥 2 的信任关系

  3. 设备递归地重复这个过程,直到它到达根域

现在如果根密钥(图中的密钥 3 )可以信任,则可以验证从 IP 地址到密钥 3 的信任关系。根密钥的哈希值始终安全地存储在设备中。在 DNSSEC 中,它被称为根信任锚。如果密钥 3 的哈希值与预先安装的锚匹配,则可以安全地建立信任链。通过信任链,www.example.org 的 IP 地址现在已经通过了身份验证。

支持 DNSSEC

如果你想在你的应用程序中要求 DNSSEC 验证,那么你需要如下操作:

  • 域名支持 IPV6

  • 对域名进行签名

  • 采用相应架构的 APIs

在只有 IPV6 的环境中,IPV4 地址被转换为合成 IPV6 地址。如果域名被签名,合成地址无法通过 DNSSEC 验证; 启用 DNSSEC 后,它们无法访问。因此,请确保域支持 IPv6。

确保你的 DNS 服务提供商使用 DNSSEC 对你的域名进行签名。如果你在您的应用程序中启用了 DNSSEC 但是没有对你的域名进行签名,为了尝试对没有签名的域名进行身份验证,DNS解析时间会增加。

获得相应的基础架构支持后,以下是为你的应用采用 DNSSEC 所需要的代码。

 //  Require DNSSEC validation in your URL request at session level.
let configuration = URLSessionConfiguration.default
configuration.requiresDNSSECValidation = true
let session = URLSession(configuration: configuration)

如果你是 URLSession 客户端,你可以要求对你的 URL 请求进行 DNSSEC 验证。举个例子:

var request = URLRequest(url: URL(string: "https://www.xxx.com")!)
request.requiresDNSSECValidation = true
let (data, response) = try await URLSession.shared.data(for: request)

先创建一个默认会话配置,设置需要 DNSSEC 验证。接下来将使用修改后的配置来创建会话。它会为从此会话创建的所有 URL 请求启用 DNSSEC。如果你不想在整个会话启用 DNSSEC,你也可以在请求级别执行此操作。

如果你是 Network.framework 客户端,你还可以要求对连接进行 DNSSEC 验证。创建参数对象时,需要进行 DNSSEC 验证,然后使用参数对象创建 NWConnection

 //  Require DNSSEC validation in your network request.
let parameters = NWParameters.tls
parameters.requiresDNSSECValidation = true
let connection = NWConnection(host: "www.example.org", port: .https, using: parameters)

当你开始连接时,只有在 DNSSEC 验证完成并且与经过验证的 IP 地址建立连接时,它才会进入就绪状态。启用 DNSSEC 后,将仅使用经过验证的地址来建立连接。

在 DNSSEC 中,验证失败不会返回任何错误。收到验证失败的响应等于没有收到任何响应。

如果存在篡改响应的 DNS 提供者,地址将无法通过身份验证检查,因此将直接丢弃。当设备加入 DNS 提供商未篡改响应的新网络时,验证将再次进行,解析将自动恢复正常。

以下是一些可能导致 DNSSEC 失败的情况。

  • 当原始 DNS 响应被修改了,不匹配的签名将无法通过 DNSSEC 检查,从而导致验证失败

  • 当设备无法访问任何预安装的信任锚并且无法与其建立信任链时

  • 网络不支持 DNSSEC 所需的必要协议,例如 DNS over TCP 和 EDNS0 选项

  • 当签名的域名不支持 IPv6 时,由互联网服务提供商提供的合成 IPv6 地址将无法通过验证

这就是使用 DNSSEC 对 DNS 响应进行身份验证的方法,但如果它们仍未加密,网络上的任何人都可以看到它们。

DDR 对 DNS 加密

iOS 14引入了加密 DNS 以帮助保护隐私。可以使用应用程序中的 NEDNSSettingsManager 或配置文件中的 DNSSettings 手动配置加密的 DNS 系统范围。还可以使用 NWParameters 上的 PrivacyContext 为您的应用程序选择加密 DNS。在 iOS 16 中新增了可以自动使用加密的 DNS的新功能。

如果您的网络支持发现指定解析器(也称为 DDR ),则 DNS 查询将自动使用 TLS 或 HTTPS。要使用加密的 DNS,您的设备需要知道解析器支持 TLS 或 HTTPS,并且可能还需要学习端口或 URL 路径。诸如 DHCP 或路由器播发等常见机制仅提供普通 IP 地址。DDR 是 Apple 与其他行业合作伙伴在 IETF 中开发的一种新协议。

它为 DNS 客户端提供了一种通过使用特殊 DNS 查询来了解这些必要信息的方法。当你的设备加入新网络时,它将发出“_dns.resolver.arpa”的服务绑定查询。如果 DNS 服务器支持 DDR,它会回复一个或多个配置。然后,设备使用此信息建立与指定解析器的加密连接。它验证未加密解析器的 IP 地址是否包含在指定解析器的 TLS 证书中。这样做是为了确保未加密的解析器和加密的解析器属于同一实体。

如果一切正常,设备现在默认使用加密的 DNS。DDR 一次适用于单个网络。只有在当前网络支持时,你的设备才会自动使用加密的 DNS。同样重要的是要注意,如果 DNS 服务器的 IP 地址是私有 IP 地址,则 DDR 不起作用。这是因为 TLS 证书中不允许此类 IP 地址,因为它们的所有权无法验证。

在 iOS 16还支持在使用加密 DNS 进行配置设置时使用 NEDNSSettingsManagerDNSSettings 配置文件,指定客户端身份验证的功能。

现在可以使用 NEDNSSettingsidentityReference 属性配置客户端证书。就像 VPN 的客户端证书一样。这些可以应用于 DNS over TLS 和 DNS over HTTPS。这是保护 DNS 的途径。

写到最后

我在招聘面试的时候,问一些似乎非客户端的知识问题时,偶尔会遇到以下的反问:

  • 作为一个客户端开发,或者说前端开发,我们为什么要去了解这些内容呢?

  • 这些内容似乎在我们的工作中不曾遇到,我们了解不了解,清楚不清楚又有什么关系呢?

当然还有一些没反问的,可能仅仅是缺了反问的勇气,内心的想法还是一样的。对于这类问题,我不好判定他们想法对不对。

在我看来,很多知识点看起来似乎在我们日常工作中用不到,但是能帮助我们从一个更宏观更全局的角度去理解我们面对的需求或者说工程项目。

跳出单个业务,跳出单个团队看问题,其实是非常重要的,而这些能力素养,其实就是在更加全面的知识积累中萌生出来的。

参考文献