Total Pageviews

Friday, 3 January 2020

用DNSSEC保护DNS流量

4.6.1. 介绍 DNSSEC

DNSSEC是一套“ 域名系统安全扩展 ”(DNSSEC,Domain Name System Security Extensions) ,能让“ 域名系统 ”(DNS,Domain Name System)客户端进行身份验证以及检查来自 DNS 域名服务器响应的完整性,以此鉴定它们的来源,并判断它们是否在传输过程中被篡改过。

4.6.2. 了解 DNSSEC

对于通过互联网连接,现在有越来越多的网站使用 超文本传输协议安全(HTTPS,Hyper Test Transfer Protocol Security) 来提供安全的链接。然而,除非您直接输入 IP 地址,在连接到 HTTPS 网络服务器之前,必须执行 DNS 查询。由于缺少身份验证,执行这些 DNS 查询是不安全的,且会遭到“ 中间人 ”攻击( MITM , man-in-the-middle attacks)。换句话说, DNS 客户端无法确信疑似来自特定的 DNS 域名服务器的应答是否可信,以及是否被篡改过。更重要的是,递归服务器无法确定从其他域名服务器获取的记录是真实的。 DNS 协议无法提供客户端可确保不遭受中间人攻击的机制。 DNSSEC 的引入解决了在使用 DNS 解析域名时,缺少身份验证和完整性检查的问题。但它不能解决机密性的问题。
DNSSEC 所发布的信息包括 DNS 资源记录的数字签字和公用加密密钥的分配,以这样的方式让 DNS 解析器建立起多层次的信息链。因所有 DNS 资源记录而生成的数字签名,添加到此 DNS 区域作为资源记录签名(RRSIG)。此区域所添加的公用加密密钥作为资源记录的域名系统密钥( DNSKEY )。要建立起多层次的信息链,则须将 DNSKEY 的散列值发布到父区域作为“ 代理签名 ”(DS,Delegation of Signing)资源记录。要验证不存在性,则须使用 NextSECureNSEC,下一代安全)和 NSEC3 (NSEC的替换或备用方案)资源记录。在 DNSSEC 区域签名中,每一个“ 资源记录集 ”(RRset,resource record set) 都有其对应的 RRSIG 资源记录。请注意, 用作子区域代理的记录(域名服务器粘附记录,NS and glue records)并没有进行签名;这些记录要显示在子区域,并在此区域进行签名。
运用 root 区域公用加密密钥进行配置的解析器会完成处理 DNSSEC 的信息。使用这种密钥,解析器可以验证用于 root 区域的签名。例如, root 区域对 .com 的 DS 记录进行签名。 root 区域也为 .com 域名服务器提供域名服务器粘附记录。解析器会跟踪代理和查询使用代理域名服务器 .com 的DNSKEY 记录。所获取的 DNSKEY 记录散列值应当与 root 区域的 DS 记录相匹配。如果匹配,则解析器将会信任所获取的 .com DNSKEY 。在 .com 区域内, RRSIG 记录是由 .com DNSKEY 所创建。同样地,在 .com 中的代理也是重复此程序,例如 redhat.com。用这种方法,尽管 DNS 验证解析器在其正常操作期间收集了很多 DNSKEY ,但只需用一个 root 密钥对其进行配置。如果密码检查失败了,则解析器将 SERVFAIL 会返回给应用程序。
DNSSEC 的设计是根据以下这种方式:对于不支持 DNSSEC 的应用程序完全不可见。如果非 DNSSEC 应用程序查询支持 DNSSEC的解析器 ,则它所接收的答复没有任何新资源记录类型,如 RRSIG 。然而, 支持 DNSSEC 的解析器仍将执行所有密码检查,若探测到恶意 DNS 答复,它仍会将 SERVFAIL 错误返回给应用程序。 DNSSEC 会保护 DNS 服务器(权威服务器和递归服务器)数据的完整性,但却不为应用程序和解析器提供的安全保护。因此,予以一个从应用程序到其解析器的安全传输方式十分重要。实现这一目的最容易的方法就是运行 localhost (本地主机)上支持 DNSSEC 的解析器,使用 /etc/resolv.conf下的 127.0.0.1 。或者可以使用虚拟专用网络( VPN ,Virtual Private Network)连接到远程 DNS 服务器。

了解热点问题

使用无线网络热点( Wi-Fi Hotspot ,Wireless Fidelity Hotspot)或 VPN 时,就会依赖 DNS 欺骗 (DNS lies)。所获取的端口往往会发生 DNS 劫持,以便重定向用户跳转到需要身份验证(或支付)的 Wi-Fi 服务网页。连接 VPN 的用户常常需使用 内部专用 DNS 服务器,以便定位那些在公司网络外不存在的资源。这需要软件进行额外处理。例如, dnssec-trigger 可用于探测一个无线热点( Hotspot )是否劫持 DNS 查询,或 unbound 是否充当代理域名服务器处理 DNSSEC 查询。

选择支持 DNSSEC 的递归解析器

要部署支持 DNSSEC 的递归解析器,则可使用 BINDunbound 。这两者在默认情况下都使用 DNSSEC ,并用 DNSSEC root 密钥进行配置。在服务器上使用 DNSSEC ,两者都可正常工作。然而, unbound 更常用于移动设备,如笔记本电脑。因为它允许本机用户对 DNSSEC 覆写动态重配置,无论是使用 dnssec-trigger 时无线热点所需求的, 亦或是使用 Libreswan 时 VPNs 所需求的。 unbound 守护进程进一步支持对列入 etc/unbound/*.d/目录的 DNSSEC 异常状况进行部署,这对服务器和移动设备都有用。

4.6.3. 了解 Dnssec-trigger

一旦 unbound 完成安装,并在 /etc/resolv.conf下进行配置,则所有来自应用程序的 DNS 查询都会通过 unbound进行处理。dnssec-trigger 只有在被触发时,才会对 unbound 解析器进行重配置。这大多数运用于漫游的客户机,如笔记本电脑,这种可连接到不同 Wi-Fi 网络的机器。其过程如下:
  • 通过 动态主机配置协议(DHCP,Dynamic host configuration protocol) 获取新的 DNS 服务器时,则 NetworkManager 会触发 dnssec-trigger
  • 随后,Dnssec-trigger 会对服务器执行一系列测试,判断其是否完全支持 DNSSEC 。
  • 如果支持,那么 dnssec-trigger 会重配置 unbound ,以用于作为所有查询转发程序的 DNS 服务器。
  • 如果测试失败,则dnssec-trigger 将忽略新的 DNS 服务器,并尝试一些可行的退却方法。
  • 如果它判定一个不受限制的 53 端口(用户数据报协议(UDP,User Datagram Protocol) 以及 传输控制协议(TCP,Transmission Control Protocol))可以使用,则它将告知 unbound 可成为全递归 DNS 服务器,无需使用任何转发程序。
  • 如果无法完成操作,如因 53 端口被防火墙阻拦,此防火墙会阻挡除连接网络的 DNS 服务器之外的所有程序,则它将会尝试通过使用 DNS 到 80 端口,亦或通过使用 DNS 封装的 安全传输层协议(TLS,Transport Layer Protocol) 到 443 端口。在 80 端口和 443 端口运行 DNS 的服务器可在 /etc/dnssec-trigger/dnssec-trigger.conf下进行配置。注释的范例可在默认配置文件中找到。
  • 如果这些退却方法也失败了,则 dnssec-trigger 将提供一种不安全的操作,这将完全忽略 DNSSEC ;亦或它将在 缓存专用 (cache only)模式下运行,此模式下它将不会尝试新的 DNS 查询,但将会应答所有已在缓存器中的数据。
无线热点更是常常在授予访问网络权限之前,重定向用户到登录页面。在探测上述编列期间,如果探测到重定向命令,则会提示用户,以询问是否通过要求登录来获取网络访问权限。dnssec-trigger 守护进程将继续对 DNSSEC 解析器每十秒进行探测。关于使用 dnssec-trigger 图形化工具的更多信息,请参阅 第 4.6.8 节 “使用 Dnssec-trigger”

4.6.4. 提供域和域名服务器的 VPN

VPN 一些连接类型可传输域和一系列域名服务器, 可用于作为 VPN 隧道安装部分的域。在 红帽企业版 Linux 中,这是由 NetworkManager 所支持的。这就是说, unbounddnssec-triggerNetworkManager 的结合产物能够完全支持 VPN 软件所提供的域和域名服务器。一旦 VPN 隧道完成,就可以清除关于所有接收域名的登录在本机的 unbound 缓存,从而通过 VPN 获取的内部域名服务器中提取最新对域名名称的查询。终止 VPN 隧道时,则会再次清除 unbound 缓存,以确保任何对域的查询会返回给公用 IP 地址,而不会返回到原先获取的 IP 地址。请参阅〈 第 4.6.11 节 “对连接所提供的域进行 DNSSEC 验证配置” 〉。

4.6.6. 了解信任锚(Trust Anchor)

信任锚由 DNS 域名以及此域名相关的公用密钥(或公用密钥的散列值)组成。其表述为一个基本的 64 比特加密密钥。其类似于一种信息交换方式的证书,含有公用密钥,可用于对 DNS 记录进行核实和身份验证。关于信任锚更加完整的定义,请参阅〈 RFC 4033 〉。

4.6.7. 安装 DNSSEC

4.6.7.1. 安装 unbound

要通过在本机上使用 DNSSEC 对 DNS 进行验证,则必须安装 DNS 解析器 unbound (或 bind )。移动设备上只需安装 dnssec-trigger 。对于服务器而言,安装unbound 就应当足够了,尽管根据服务器所在地(局域网(LAN,local area network) 或 Internet),可能会要求对本地域进行转发配置。 dnssec-trigger 当下只在全球公共 DNS 区域提供帮助。NetworkManagerdhclient, 以及 VPN 应用程序通常可以自动收集域列表(和域名服务器列表),但 dnssec-triggerunbound 却不行。
要安装 unbound ,则须作为 root 用户允许以下命令:
~]# yum install unbound

4.6.7.2. 检查 unbound 是否在运行

要判定 unbound 守护进程是否在运行,则须输入以下命令:
~]$ systemctl status unbound
 unbound.service - Unbound recursive Domain Name Server
   Loaded: loaded (/usr/lib/systemd/system/unbound.service; disabled)
   Active: active (running) since Wed 2013-03-13 01:19:30 CET; 6h ago
systemctl status 命令将会报告 unbound Active: inactive (dead) ,若 unbound 服务未在运行。

4.6.7.3. 启动 unbound

要启动 unbound 守护进程用于当前会话,则须作为 root 用户运行以下命令:
~]# systemctl start unbound
运行 systemctl enable 命令,以确保每次启动系统时, unbound 开始运行:
~]# systemctl enable unbound
unbound 守护进程允许使用以下目录对本地数据或覆写进行配置:
  • /etc/unbound/conf.d 用于为特定的域名添加配置。这用于重定向域名查询到特定的 DNS 服务器。这通常用于只存在于企业广域网(WAN,wide area network)的子域。
  • /etc/unbound/keys.d 目录用于为特定的域名添加信任锚。这在 DNSSEC 对内部专用域名进行签名时才需要,但并没有公用现有的 DS 记录来建立信任途径。另一种使用情况是,当对内部域进行签名时,使用不同的 DNSKEY ,而不是使用企业广域网之外可行的公用域名。
  • /etc/unbound/local.d 目录用于添加特定的 DNS 数据作为本地覆写。者可用于建立黑名单,或创建手动覆写。这个日期将会通过 unbound 返回给客户端,但是不会被标记为有 DNSSEC 签名。
NetworkManager 和一些 VPN 软件可改变动态配置。这些配置目录含有注释范例。更多信息请参阅 unbound.conf(5) 手册页。

4.6.7.4. 安装 Dnssec-trigger

dnssec-trigger 应用程序作为 dnssec-triggerd守护进程来运行。要安装 dnssec-trigger ,则须作为 root 用户运行以下命令:
~]# yum install dnssec-trigger

4.6.7.5. 检查 Dnssec-trigger 守护进程是否在运行

要判定 dnssec-triggerd 是否在运行,则须输入以下命令:
~]$ systemctl status dnssec-triggerd
systemctl status dnssec-triggerd.service
dnssec-triggerd.service - Reconfigure local DNS(SEC) resolver on network change
   Loaded: loaded (/usr/lib/systemd/system/dnssec-triggerd.service; enabled)
   Active: active (running) since Wed 2013-03-13 06:10:44 CET; 1h 41min ago
systemctl status 命令将会报告 dnssec-triggerd Active: inactive (dead) ,若 dnssec-triggerd 守护进程未在运行。要在当前会话中启用,则须作为 root 用户运行以下命令:
~]# systemctl start dnssec-triggerd
运行 systemctl enable 命令,以确保每次启动系统时, unbound 开始运行:
~]# systemctl enable dnssec-triggerd

4.6.8. 使用 Dnssec-trigger

dnssec-trigger 应用程序有GNOME panel 的功能,用于显示 DNSSEC 探测结果,以及用于执行 DNSSEC 探测命令请求。要启用此功能,则须按 Super 键进入应用程序概览视图( Activities Overview),输入 DNSSEC ,然后再按 Enter 。一个形似船锚的图标将会添加到屏幕底部的消息托盘。可通过按屏幕底部右侧的蓝色圆形通知图标来显示。右击锚状图标,则会出现弹出式菜单。
正常操作下, unbound 在本机可用作域名服务器, resolv.conf 会指向 127.0.0.1。当您在 无线热点登录(Hotspot Sign-On) 界面点击 OK 时,这就会改变。 DNS 服务器受到 NetworkManager 的查询,并被放入 resolv.conf。然后您就可以在无线热点登录页面进行身份验证。锚状图标会显示巨大的红色感叹号以作警示,提醒您 DNS 查询的执行并不安全。身份验证后, dnssec-trigger 可自动检测,并转换到安全模式。尽管在某些情况下,它无法自动检测,则用户必须手工操作,选择 重新检测(Reprobe)
正常情况下,Dnssec-trigger 不需要用户进行交互操作。一旦 启用,它会在后台工作。如果出现问题,它会弹出消息框来通知用户。它也会将 resolv.conf 文件的变更通知 unbound

4.6.9. 对 DNSSEC 使用 dig 命令

要查看 DNSSEC 是否在工作,则可用不同的命令行工具。最好的使用工具就是 bind-utils 工具包中的 dig 命令。其他可用的工具分别是, ldns 工具包中的 drillunbound 工具包中的 unbound-host 。旧版的 DNS 实用程序 nslookuphost 都已过时,不应再使用。
要使用 dig 发送 DNSSEC 数据查询请求,则须添加 +dnssec 选项到命令中,例如:
~]$ dig +dnssec whitehouse.gov
; <<>> DiG 9.9.3-rl.13207.22-P2-RedHat-9.9.3-4.P2.el7 <<>> +dnssec whitehouse.gov
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- 0="" 127.0.0.1="" 1="" 20130822114016="" 20130825124016="" 2013="" 20="" 21388="" 227="" 22:01:52="" 22="" 233="" 2="" 3oi=";;" 4096="" 72.246.36.110="" 7="" 8399="" a="" ad="" additional:="" answer:="" answer="" aug="" authority:="" bb8vhwekiakpalprt3hq1gkjdrovkmjytbxighuki="" do="" edns:="" edt="" elfmqqcaq3se66iyh0jem7htgpeue1zc="" flags:="" hh0377i0lsybj="" id:="" in="" lw6gn8gpikqhztakgmxddmq2iarp6p="" msec="" msg="" n3poigyrftxr="" noerror="" opcode:="" opt="" pre="" pseudosection:="" qr="" query:="" query="" question="" ra="" rcvd:="" rd="" rrsig="" section:="" server:="" size="" status:="" thu="" time:="" udp:="" uzv5hl4uwwd="" version:="" wbmokbsuuv6ngut1wwwpbi="" when:="" whitehouse.gov.="">
    除 A 记录之外,所返回的 RRSIG 记录含有 DNSSEC 签名以及签名的初始时间和截止时间。据 unbound 服务器显示,通过返回的顶端 flags: 区段下 ad 比特可知数据已经过 DNSSEC 身份验证。
  
如果 DNSSEC 验证失败,则 dig 命令将会返回 SERVFAIL 错误:
~]$ dig badsign-a.test.dnssec-tools.org
; <<>> DiG 9.9.3-rl.156.01-P1-RedHat-9.9.3-3.P1.el7 <<>> badsign-a.test.dnssec-tools.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- 0="" 1010="" 127.0.0.1="" 1284="" 1="" 2013="" 22:04:52="" 22="" 4096="" 60="" a="" additional:="" answer:="" aug="" authority:="" badsign-a.test.dnssec-tools.org.="" edns:="" edt="" flags:="" id:="" in="" msec="" msg="" opcode:="" opt="" pre="" pseudosection:="" qr="" query:="" query="" question="" ra="" rcvd:="" rd="" section:="" server:="" servfail="" size="" status:="" thu="" time:="" udp:="" version:="" when:="">

  
要请求查看关于失败的更多信息,则须指定 +cd 选项加入到 dig 命令中,以禁止 DNSSEC 检查:
~]$ dig +cd +dnssec badsign-a.test.dnssec-tools.org
; <<>> DiG 9.9.3-rl.156.01-P1-RedHat-9.9.3-3.P1.el7 <<>> +cd +dnssec badsign-a.test.dnssec-tools.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- 0="" 127.0.0.1="" 19442="" 1="" 20130820173720="" 20130919183720="" 2013="" 22:06:31="" 22="" 257="" 26065="" 2="" 4096="" 49="" 4="" 5="" 75.119.216.33="" 86400="" a="" additional:="" answer:="" answer="" aug="" authority:="" badsign-a.test.dnssec-tools.org.="" cd="" do="" e572dlkmvyb4cgtryahikkevdop7tockqb7hxfnzkvbfxbzjoidrejrr="" edns:="" edt="" flags:="" id:="" in="" kyw=";;" msec="" msg="" n2nppctn2kubn5ur1bjrin3gqy20lzlzx2kd7czbtiemsu="" noerror="" opcode:="" opt="" pre="" pseudosection:="" qr="" query:="" query="" question="" ra="" rcvd:="" rd="" rrsig="" section:="" server:="" size="" status:="" test.dnssec-tools.org.="" thu="" time:="" udp:="" unyhcsc0="" version:="" when:="" zcgafj2hykfy0yjhalnuqvm0s6xonnbsvc2xliybjdftan6ksr0yfdyz="">

  
通常, DNSSEC 错误会自己显示错误的初始时间或截止时间。尽管在本例中, www.dnssec-tools.org 的访问者蓄意损坏 RRSIG 签名,这是我们无法通过手动查看此输出来进行探测。错误将会显示在 systemctl status unbound 的输出中,且 unbound 守护进程会将这些错误记录到 syslog ,如下所示:
Aug 22 22:04:52 laptop unbound: [3065:0] info: validation failure badsign-a.test.dnssec-tools.org. A IN
使用 unbound-host的示例:
~]$ unbound-host -C /etc/unbound/unbound.conf -v whitehouse.gov
whitehouse.gov has address 184.25.196.110 (secure)
whitehouse.gov has IPv6 address 2600:1417:11:2:8800::fc4 (secure)
whitehouse.gov has IPv6 address 2600:1417:11:2:8000::fc4 (secure)
whitehouse.gov mail is handled by 105 mail1.eop.gov. (secure)
whitehouse.gov mail is handled by 110 mail5.eop.gov. (secure)
whitehouse.gov mail is handled by 105 mail4.eop.gov. (secure)
whitehouse.gov mail is handled by 110 mail6.eop.gov. (secure)
whitehouse.gov mail is handled by 105 mail2.eop.gov. (secure)
whitehouse.gov mail is handled by 105 mail3.eop.gov. (secure)

4.6.10. 装配 Dnssec-trigger 无线热点探测设备

连接网络时, dnssec-trigger 会尝试探测无线热点。无线热点通常是一种会在可使用网络之前迫使用户进行网页交互的设备。通过尝试下载一已知内容的指定网页,来完成探测。如果存在无线热点,则不会有如预期所料的接收内容。
要设置一已知的固定网页,使其可通过 dnssec-trigger 用于探测无线热点,则须如下执行:
  1. 对某些在互联网上可公开访问的机器,设置其网页服务器。关于网页服务器的更多信息,请参阅《Red Hat Enterprise Linux 7 系统管理员指南 》。
  2. 一旦您让服务器开始运行,则会将已知内容的静态页面发布到服务器上。此页面无需一定是有效的 HTML 页面。例如,您可使用一个名为 hotspot.txt只含有 OK 字符串的纯文本文件。假设您的服务器位于 example.com ,您可将 hotspot.txt 文件发布到网页服务器的 document_root/static/ 子目录,那么您静态网页服务器的地址将是 example.com/static/hotspot.txt 。请参阅《 Red Hat Enterprise Linux 7 系统管理员指南 》下的 DocumentRoot 指令。
  3. 将以下命令行添加到 /etc/dnssec-trigger/dnssec-trigger.conf 文件:
    url: "http://example.com/static/hotspot.txt OK"
    此命令添加了一个可通过 HTTP (80端口)探测到的 URL 。第一部分就是可解析的 URL 以及可下载的页面。命令的第二部分是所下载的网页预期含有的文本字符串。
关于配置选取的更多信息,请参阅手册页的 dnssec-trigger.conf(8)

4.6.11. 对连接所提供的域进行 DNSSEC 验证配置

在默认情况下,转发区及其固有的域名服务器会通过 dnssec-trigger 自动添加到 unbound ,以用于任何通过 NetworkManager 的连接所提供的域,除了 Wi-Fi 连接之外。默认情况下,所有添加到 unbound 的转发区都已进行 DNSSEC 验证。
用于验证转发区的默认行为可被更改,从而所有的转发区在默认情况下将 会进行 DNSSEC 验证。要做到这一点,则须更改 dnssec-trigger 配置文件 /etc/dnssec.conf 下的 validate_connection_provided_zones 变量。作为 root 用户,打开并编辑以下命令行:
validate_connection_provided_zones=no
无法更改任何现有的转发区,只能更改未来的转发区。因此,如果您想禁止 DNSSEC 用于当前所提供的域,那么您需要重新连接。

4.6.11.1. 对 Wi-Fi 所提供的域进行 DNSSEC 验证配置

为 Wi-Fi 所提供的区域添加转发区即可启用。要实现此功能,则须更改 dnssec-trigger 配置文件 /etc/dnssec.confadd_wifi_provided_zones 变量。作为 root 用户,打开并编辑以下命令行:
add_wifi_provided_zones=yes
对任何已存在的转发区无法进行更改,只能对将要执行的转发区进行更改。因此,如果您要禁止 DNSSEC 用于当前 Wi-Fi 所提供的域,那么您需要重新连接(重新开启) Wi-Fi 。
警告
要“ 打开 ”添加到 unbound 作为转发区的 Wi-Fi 所提供的域,则可能会出现安全隐患,例如:
  1. 一个 Wi-Fi 接入点可能有意通过 DHCP( Dynamic host configuration protocol,动态主机配置协议) 给您提供一个域,而它并无 DHCP 的权限,也无法将您所有的 DNS 查询发送到其 DNS 服务器。
  2. 如果您对“ 关闭 ”的转发区进行 DNSSEC 验证,那么 Wi-Fi 所提供的 DNS 服务器可从所提供的域中,伪造用于域名的 IP 地址,而您并不知情。

4.6.12. 附加资源

以下这些资源将对 DNSSEC 进行更多的 解释。

4.6.12.1. 已安装的文档

  • dnssec-trigger(8) 手册页 —— 描述用于 dnssec-triggerd, dnssec-trigger-control 以及 dnssec-trigger-panel 的命令选项。
  • dnssec-trigger.conf(8) 手册页 —— 描述用于 dnssec-triggerd 的配置选项。
  • unbound(8) 手册页 —— 描述用于 unbound 以及 DNS 验证解析器的命令选项。
  • unbound.conf(5) 手册页 —— 含有配置 unbound 的信息。
  • resolv.conf(5) 手册页 —— 含有解析器例程所读取的信息。

4.6.12.2. 在线文档

http://tools.ietf.org/html/rfc4033
RFC 4033 DNS 安全介绍及其要求( DNS Security Introduction and Requirements)。
http://www.dnssec.net/
有链接到许多 DNSSEC 资源的网站。
http://www.dnssec-deployment.org/
DNSSEC 部署计划( DNSSEC Deployment Initiative)由国土安全部赞助( Department for Homeland Security),含有大量 DNSSEC 信息,并通过 邮件列表来讨论 DNSSEC 部署事项。
http://www.internetsociety.org/deploy360/dnssec/community/
国际互联网大会(Internet Society)的 Deploy 360 计划是为了促进并协调 DNSSEC 部署,这是在全球范围内发现团体和 DNSSEC 活动的良好资源。
http://www.unbound.net/
此文档含有关于 unbound DNS 服务的基本信息。
http://www.nlnetlabs.nl/projects/dnssec-trigger/
此文档含有关于 dnssec-trigger 的基本信息。 
from https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/7/html/security_guide/sec-securing_dns_traffic_with_dnssec
--------

DNS的安全及隐私

DNS设计之初并没有考虑安全问题,所以大部分DNS查询使用UDP传输,当然也可以用TCP。这两种方式既没有加密也没有签名。这就意味着中间人可以监听到用户访问的域名,导致隐私泄露。另外因为没有签名验证,中间人也可以篡改DNS返回的IP地址,导致用户访问钓鱼网站。后来有了DNSSEC,引入了签名机制,保证了从权威DNS服务器,到DNS递归服务器,再到客户端都没有被篡改。但是这依然没有解决隐私问题。
其实隐私问题之所以没被重视,主要有几点:第一,中间人肯定能知道你要访问的服务器IP地址,多数情况下知道IP就知道是什么网站了。第二,有一些上层协议也会泄露域名,明文HTTP就不说了,TLS协议也有Server Name Indication(SNI),会暴露明文域名。(注:IETF TLS工作组目前正在探讨草案《SNI Encryption in TLS Through Tunneling》,计划加密SNI)。第三,一些上层协议,如TLS,能够识别DNS是否被篡改,这使得签名DNS本身显得不那么重要。[1]
但即使有上述三点,加密DNS数据也有显而易见的好处。第一,减小攻击面。第二,用户请求DNS之后,未必就非得访问它呀,比如本文下述的子域名爆破,我们只对DNS数据本身感兴趣,而不访问其域名,这样加密DNS就有了实际意义。
所以本文就要探讨一下DNS over TLS和DNS over HTTPS。这两个协议目前仅限于用户客户端和DNS递归服务器间的通信。截至2018年4月,递归服务器和权威服务器之间的通信不在这两个协议的适用范围内。也许以后递归服务器和权威服务器也会纳入DNS over TLS协议里,不过目前我没听说有人实现它。
----------------------------------------------------

DNSSEC 简介,签名的工作原理

DNSSEC 是一组使域名解析系统(DNS)更加安全的解决方案。1993 年,IETF 展开了一个关于如何使 DNS 更加可信的公开讨论,最终决定了一个 DNS 的扩展——DNSSEC,并于 2005 年正式发布。然而,实际推行 DNSSEC 是一件非常难的事情,本文将讨论一下现有 DNS 系统所存在的一些不安全性,以及 DNSSEC 是如何解决这些问题的。

基础

传统 DNS 的问题

上一篇文章中已经知道,在你访问一个网站,比如 www.example.com 时,浏览器发送一个 DNS 消息到一个 DNS 缓存服务器上去查询,由于 DNS 系统的庞大,这中间还需要经过好几层 DNS 缓存服务器。想要正确访问到这个网站,就需要这所有的缓存服务器都要正确的响应。

DNS 的中间人攻击

DNS 查询是明文传输的,也就是说中间人可以在传输的过程中对其更改,甚至是去自动判断不同的域名然后去做特殊处理。即使是使用其他的 DNS 缓存服务器,如 Google 的 8.8.8.8,中间人也可以直接截获 IP 包去伪造响应内容。由于我所在的国家就面临着这个问题,所以我可以轻松的给大家演示一下被中间人攻击之后是什么个情况:
$ dig +short @4.0.0.0 facebook.com
243.185.187.39
向一个没有指向任何服务器的 IP 地址:4.0.0.0 发送一个 DNS 请求,应该得不到任何响应。可是实际上在我所处的国家却返回了一个结果,很明显数据包在传输过程中“被做了手脚”。所以如果没有中间人攻击,效果是这样的:
$ dig +short @4.0.0.0 facebook.com
;; connection timed out; no servers could be reached
DNS 系统就是这么脆弱,和其他任何互联网服务一样,网络服务提供商、路由器管理员等均可以充当“中间人”的角色,来对客户端与服务器之间传送的数据包进行收集,甚至替换修改,从而导致客户端得到了不正确的信息。然而,通过一定的加密手段,可以防止中间人看到在互联网上传输的数据内容,或者可以知道原始的数据数据是否被中间人修改。

从密码学开始

讲到 DNSSEC,就不得不讲到一些密码学的知识。这里从最基本的密码学开始讲起。 密码学主要分为三大类,这里也列出每一列常用的加密算法:
  • 对称密码学:AES、DES
  • 公钥密码学:RSA、ECC
  • 数据完整性的算法:SHA、MD5
在 DNSSEC 中,主要使用到的是公钥密码学和数据完整性算法两种加密学。

公钥密码学实现数字签名

公钥密码主要是与对称密码进行区分:对称密码的加密与解密使用相同的密钥;而公钥密码使用的加密密钥叫做公钥,解密密钥叫做私钥——两种密钥相对独立,不能替代对方的位置,而且知道公钥无法推出私钥。这两种密码学都必须是可逆的(所以解密算法可以看作加密算法的逆)。以函数的形式表达的话如下:

对称密码

  • 密文 = 加密算法(密钥, 原文)
  • 原文 = 解密算法(密钥, 密文)

公钥密码

  • 密文 = 加密算法(公钥, 原文)
  • 原文 = 解密算法(私钥, 密文)
当然,如果私钥充当公钥,公钥充当私钥,那么就是这样的:
  • 密文 = 加密算法(私钥, 原文)
  • 原文 = 解密算法(公钥, 密文)
假如服务器要向客户端发送一段消息,服务器有私钥,客户端有公钥。服务器使用私钥对文本进行加密,然后传送给客户端,客户端使用公钥对其解密。由于只有服务器有私钥,所以只有服务器可以加密文本,因此加密后的文本可以认证是谁发的,并且能保证数据完整性,这样加密就相当于给记录增加了**数字签名**。但是需要注意的是,由于公钥是公开的,所以数据只是不能被篡改,但可以被监听。 此处的服务器如果是充当 DNS 服务器,那么就能给 DNS 服务带来这个特性,然而一个问题就出现了,如何传输公钥?如果公钥是使用明文传输,那么攻击者可以直接将公钥换成自己的,然后对数据篡改。
中间人攻击
中间人攻击
所以,一个解决的方法是使用一个被公认的公钥服务器,客户端的操作系统中在本地先存好这个公钥服务器自身的公钥。当与服务器通信时,客户端从这个被公认的公钥服务器通信,用户使用操作系统中内置的公钥来解密获得服务器的公钥,然后再与服务器通信。
公钥服务器
公钥服务器
然而 DNS 是一个庞大的系统,在这个系统中根域名服务器充当了被公认的公钥服务器,其中每一个一级域名服务器也是一个子公钥服务器。最后一张图,就是 DNSSEC 的基本雏形了。
公钥服务器系统
公钥服务器系统

数据完整性算法,减轻公钥密码的运算压力

在密码学中,还存在一种检查数据完整性的算法,其 “加密” 无须密钥,密文不可逆(或很难求逆),而且密文与原文不是一一对应的关系。而且,通过此算法算出的密文通常是一个固定长度的内容。通过此算法算出的密文叫做哈希值。在 DNSSEC 里所运用到它的特性是:原文一旦修改,密文就会发生变化。 公钥密码学存在的一个很重要的问题:加密和解密的速度相对于对称密码太慢了。所以想要提高性能,就需要减短需要加密和解密的文本。如果只是对文本的哈希值加密,由于长度的减短,加密速度就能大大提高。在服务器传送时,同时传送明文的文本和使用私钥加密的文本哈希值;客户端只需要先算出收到的明文文本的哈希值,然后再用公钥解密密文,验证两个值是否相等,依然能够防止篡改。 在 DNSSEC 中就运用了这种方法,无论是对密钥还是记录的加密。

DNSSEC

DNSSEC 这一个扩展可以为 DNS 记录添加验证信息,于是缓存服务器和客户端上的软件能够验证所获得的数据,可以得到 DNS 结果是真是假的结论。上一篇文章讲到过 DNS 解析是从根域名服务器开始,再一级一级向下解析,这与 DNSSEC 的信任链完全相同。所以部署 DNSSEC 也必须从根域名服务器开始。本文也就从根域名服务器讲起。

与 HTTPS 的区别

DNSSEC 和 HTTPS 是两个完全不同的东西,但是这里只是对其加密方式对比。即 DNSSEC 的加密方式与 TLS 进行对比。

信任链机制的不同

在配置 DNSSEC 的时候,如果与 HTTPS 比较,可以看出来:证书和私钥全部都是在自己的服务器上直接生成的,也就意味着这是 “自签名的”,不需要任何 “根证书颁发商”。二级域名所有者向一级域名注册商提交自己的公钥的哈希值,然后一级域名注册商就会给你的哈希值进行签名,从而也能形成一道信任链,远比 HTTPS 的信任链简单,操作系统也再不用内置那么多个 CA 证书,只需要一个根域名的 DS 记录即可。个人认为这是一个更先进的模式,但是它需要客户端一级一级的去依次解析,于是受到了速度的影响;HTTPS 则是直接由一个服务器返回整条证书链,与服务器进行 HTTPS 的连接时只需要与一个服务器通信。不过,DNS 记录是可以被缓存的,所以能够一定程度上的减少 DNSSEC 的延迟。

只签名,不加密

你发往 DNS 服务器的请求是明文的,DNS 服务器返回的请求是带签名的明文。这样 DNSSEC 只是保证了 DNS 不可被篡改,但是可以被监听,所以 DNS 不适合传输敏感信息,然而实际上的 DNS 记录几乎都不是敏感信息。HTTPS 的话会同时签名和双向加密,这样就能够传输敏感信息。 DNSSEC 的只签名,不加密主要是因为 DNSSEC 是 DNS 的一个子集,使用的是同一个端口,所以是为了兼容 DNS 而作出的东西,而 DNS 是不需要客户端与服务器建立连接的,只是客户端向服务器发一个请求,服务器再向客户端返回结果这么简单,所以 DNS 都可以使用 UDP 来传输,不需要 TCP 的握手,速度非常快。HTTPS 不是 HTTP 的子集,所以它使用的是另一个端口,为了做到加密,需要先与浏览器协商密钥,这之间进行了好几次的握手,延迟也上去了。

在哪里验证?

刚才所讲述的所有情况,都是在没有 DNS 缓存服务器的情况下。如果有 DNS 缓存服务器呢? 实际上,一些 DNS 缓存服务器就已经完成了 DNSSEC 验证,即使客户端不支持。在缓存服务器上验证失败,就直接不返回解析结果。在缓存服务器进行 DNSSEC 验证,几乎不会增加多少延迟。 但这也存在问题,如果缓存服务器到客户端之间的线路不安全呢?所以最安全的方法是在客户端上也进行一次验证,但这就会增加延迟了

DNSSEC 的时效性和缓存

DNSSEC 相比 HTTPS 的一个特性就是 DNSSEC 是可以被缓存的,而且即使是缓存了也能验证信息的真实性,任何中间人也无法篡改。然而,既然能够缓存,就应该规定一个缓存的时长,并且这个时长也是无法篡改的。 签名是有时效性的,这样客户端才能够知道自己获得到的是最新的记录,而不是以前的记录。假如没有时效性,你的域名解析到的 IP 从 A 换到了 B,在更换之前任何人都可以轻易拿到 A 的签名。攻击者可以将 A 的签名保存下来,当你更换了 IP 后,攻击者可以继续篡改响应的 IP 为 A,并继续使用原本 A 的签名,客户端也不会察觉,这并不是所期望的。 然而在实际的 RRSIG 签名中,会包含一个时间戳(并非 UNIX 时间戳,而是一个便于阅读的时间戳),比如 20170215044626,就代表着 UTC 2017-02-15 04:46:26,这个时间戳是指这个记录的失效时间,这意味着在这个时间之后,这个签名就是无效的了。时间戳会被加进加密内容中去参与签名的计算,这样攻击者就无法更改这个时间戳。由于时间戳的存在,就限制了一个 DNS 响应可以被缓存的时长(时长就是失效时间戳减去当前时间戳)。然而,在有 DNSSEC 之前,控制缓存时长是由 TTL 决定的,所以为了确保兼容性,这两个时长应该是完全一样的。 通过这样做,即能够兼容现有的 DNS 协议,有能够保证安全,还能够利用缓存资源加快客户端的请求速度,是一个比较完美的解决方案。

DNSSEC 的实际部署

其实,即使完全不了解,或者没看懂上面的内容,也不影响你去部署 DNSSEC,只不过你应当非常仔细的对待它,稍有不慎可能导致用户无法解析的情况。

使用第三方 DNS(Managed DNS)部署 DNSSEC

由于是使用第三方的 DNS,部署 DNSSEC 就必须需要第三方支持。常见的支持 DNSSEC 的第三方 DNS 有 Cloudflare、Rage4、Google Cloud DNS(需要申请)、DynDNS 等。开启 DNSSEC 前首先需要在第三方服务上激活 DNSSEC,这样第三方 DNS 才会返回关于 DNSSEC 的记录。 在第三方的 DNS 上激活了 DNSSEC 之后,第三方会给你一个 DS 记录,大概长这样:
tlo.xyz. 3600 IN DS 2371 13 2 913f95fd6716594b19e68352d6a76002fdfa595bb6df5caae7e671ee028ef346
此时,你就需要前往域名注册商,给你的域名提供这个 DS 记录(有些域名注册商可能不支持添加 DS 记录,此时你可以考虑转移到本站的域名注册商或者其他支持 DS 记录的注册商。此外,一些域名后缀也不支持添加 DS 记录,建议你使用主流后缀,如 .com 等,此处就以本站的域名注册商为例):
在域名注册商配置 DNSSEC
在域名注册商配置 DNSSEC
添加然后保存,一切就 OK 了。注意关键标签(Key Tag)就是 DS 记录里的第一项(此处对应的是 2371),算法(Algorithm)就是第二项(此处对应的是 13),算法类型(Digest Type)就是第三项(此处对应的是 2),整理分类(Digest)就是最后一项。剩下的内容不需要填写。 有的第三方 DNS(比如 Rage4)会给你一下子提供多个 DS 记录(相同的关键标签但是不同的算法和算法类型),然而你不需要都填写上。我建议只填写使用算法 13 与类型 2 或者算法 8 类型与类型 2 的 DS。这两个分别是 Cloudflare 推荐的参数和根域名目前所使用的参数。填写多个 DS 记录不会给你带来多少的安全性提升,但可能会增大客户端的计算量。

使用自建 DNS 部署 DNSSEC

使用自建 DNS 首先需要先生成一对密钥对,然后将其添加到 DNS 服务中去。我已经介绍了关于 PowerDNS 的添加 DNSSEC 的方法。 在此之后,你需要生成 DS 记录,通常你生成 DS 记录也是很多个,和第三方 DNS 一样,我建议只向域名注册商提交使用算法 13 与类型 2 或者算法 8 类型与类型 2 的 DS。

参考资料

安全性相关记录

在 DNS 中,有一些是安全性相关的记录,比如 DS、TLSA、CAA、SSHFP、IPSEC、以及一些通过 TXT 记录来实现的记录等。安全性相关的记录类型十分建议包含签名,也就是说安全性相关的记录应该使用 DNSSEC。此外,当一个域名下不包含这种记录类型时,也必须返回 NSEC 记录并签名。之前一篇文章中所介绍的 DS 就是一个例子。除了 DS 外,还有这些记录类型:

TLSA - DANE 相关

DANE 可以用于绑定某个域名下的某个服务的证书,也就是说可以让一些原本被客户端信任的证书不被信任,证书颁发商未经网站管理人授权签发的证书可以不被信任,可以实现和 Certificate Transparency 类似的效果。这容易与 HPKP Pin 混淆。HPKP Pin 后者只能使用于 HTTPS 服务,且只有成功且没有劫持的访问过才有效果(所以为了使 HPKP Pin 达到真正安全,必须需要建立一个受信任的中央服务器去 Preload 这些记录,类似 HSTS);DANE 即使是在第一次访问也无法被劫持,而且可以用于 Mail 等域名相关的 SSL 服务,不只限于 HTTPS。 我认为 DANE 的真正有意思的地方是在于它可以让客户端去有选择的信任自签名的证书,也就是说可以让一些原本不被客户端信任的证书被信任:通过 DNS 的方式向浏览器告知这个网站自签名证书的公钥,由于包含了签名,浏览器就能够知道这是域名所有者的公钥,就能够在这个域名下信任这个自签名的证书。这打破了目前常用的 CA 机制,网站管理者也再也不用去向 CA 花钱或者是不花钱的申请证书,而是直接使用自签名证书甚至是自己管理的 CA 签发的证书,操作系统也不再需要选择去信任哪些根证书,也能避免传统证书签发商系统存在结构性缺陷(比如证书签发商通过自己签发证书来进行 HTTPS 中间人等)。然而实现这一步首先需要客户端的支持,已经开始有程序开始支持,然而却还没有看到浏览器去支持的迹象。 使用了自签名证书的 HTTPS 且配合了 DANE 的站点与常规 HTTPS 站点的信任链对比:
  • DANE 自签名 (dane-self-ca.tloxygen.com):内置的 DS 记录 -> 根域名(.)-> 一级域名(.com)-> 二级域名(tloxygen.com)-> 自签名证书(dane-self-ca.tloxygen.com)
  • 证书颁发商签名(dane-trusted-ca.tloxygen.com):内置的根证书(DST Root CA X3)-> 中间证书(Let’s Encrypt Authority X3)-> 域名证书(dane-trusted-ca.tloxygen.com)
注:域名 tloxygen.com 只是在本地环境中进行的测试,公网无法访问
实现 DANE 的方式主要是通过 TLSA 记录: TLSA 记录包含了证书的哈希值,或者是某一个中间证书的哈希值(或者也可以是完整的证书)。同时,它可以针对不同的协议和端口配置不同的 TLSA 记录。我认为,TLSA 是最安全的一种 DANE 的方式。 你可以在这个网站生成一个 TLSA 记录,我的 dane-trusted-ca.tloxygen.com 站点绑定了 Let’s Encrypt 的中间证书,设置的 TLSA 记录是这样的:
_443._tcp.dane-trusted-ca.tloxygen.com. 604800 IN TLSA 0 0 1 25847D668EB4F04FDD40B12B6B0740C567DA7D024308EB6C2C96FE41D9DE218D
这里记录中的第一项(这里是 0)代表着 PKIX-TA,TA 意味着这个根证书或是中间证书必须在这个域名所使用的证书链中,也就是说这个域名只能使用某一个证书颁发商颁发的证书。如果第一项填写 1,代表着 PKIX-EE,EE 意味着这个证书必须是域名所使用的证书,也就是说每次更换证书后都得修改这个记录。PKIX 意味着这个证书链是受操作系统信任的,在使用证书颁发商颁发的证书时(如 Let’s Encrypt),应该使用 PKIX。 当第一项为 2 和 3 时,一切就变的有意思多了,2 代表着 DANE-TA,代表着绑定一个自签名的根证书,我的 dane-self-ca.tloxygen.com 站点就绑定了一个自签名的 Root,设置的 TLSA 是这样的:
_443._tcp.dane-trusted-ca.tloxygen.com. 604800 IN TLSA 0 0 1 25847D668EB4F04FDD40B12B6B0740C567DA7D024308EB6C2C96FE41D9DE218D
所以如果客户端支持了 DANE,那么这个自签名的根证书在这个域名下就是被信任的。 当第一项为 3 时,代表着 DANE-EE,这可以直接绑定域名证书,意味着不但可以使用自签名的证书,连证书链都免去了,我的 dane-self-signed.tloxygen.com 就直接使用了一个自签名的证书,设置的 TLSA 是这样的:
_443._tcp.dane-self-signed.tloxygen.com. 604800 IN TLSA 3 0 1 BF617DDCC4F39BD0C9228FC0C1EAD5E96252F36FB3FB5AB0CBB9B9B09C3CFE21
你可以在这个网站找到更多的不同种类的支持 TLSA 的网站。 那么问题就来了,为什么现有的浏览器都不支持 TLSA 呢?我认为主要原因如下:
  1. 会给浏览器带来一个额外的 DNS 查询时间,为了最高的安全性,浏览器应该在开始加载 HTTPS 页面(建立握手后)之前就先查询 TLSA 记录,这样才能够匹配证书是否该被信任,这样无疑增加了所有 HTTPS 页面的加载时长。
  2. 现有的 DNS 是一个不是足够稳定的东西,何况 DNSSEC 的记录类型不被一些 DNS 递归服务器所支持等,经常会由于因为种种原因遇到查询失败的问题,按照规则,TLSA 记录查询失败的话客户端也应该不加载页面。这样无疑增加了 HTTPS 页面加载的出错率,这样无疑会导致很多原本没有被中间人的网站也无法加载。
  3. DNS 污染,在一些情况下,客户端所处的是被 DNS 污染的环境,特点就是将服务器解析到了错误的 IP 地址。然而此时中间人为了仍能够让用户访问到 HTTPS 网站,会进行类似 SNI Proxy 的操作,此时的客户端访问网站仍是比较安全的。然而显然,DANE 在 DNS 被污染后,会直接拒绝加载页面,这样被 DNS 污染的环境会有大量站点无法加载
  4. DNSSEC 本身的安全性,现在仍有一些 DNSSEC 的 ZSK 或者是 KSK 在使用 1024-bit 的 RSA,在证书系统中,1024-bit 的 RSA 已经基本不被信任,而 RFC 却建议使用 1024-bit 直到 2022 年。而这不是主要问题,越来越多的域名服务器,尤其是根域名和一级域名,都已经使用了 2048-bit 的 RSA 甚至是 256 位的 ECDSA,客户端可以直接拒绝接受 1024-bit RSA。
然而我认为这些都不是什么问题。为了解决 DNS 的问题,可以使用 Google DNS-over-HTTPS,这样的话就能避免很多 DNS 污染的问题,而且由于 DNSSEC 本身包含签名,Google 是无法对返回内容篡改的。那么直至现在,TLSA 就只存在最后一个问题:为了获取 TLSA 记录而增加的加载延迟。而这也可以完美解决,OCSP 就是一个例子,现在传统的 CA 为了实现吊销机制,也都有 OCSP:
OCSP(Online Certificate Status Protocol,在线证书状态协议)是用来检验证书合法性的在线查询服务,一般由证书所属 CA 提供。为了真正的安全,客户端会在 TLS 握手阶段进一步协商时,实时查询 OCSP 接口,并在获得结果前阻塞后续流程。OCSP 查询本质是一次完整的 HTTP 请求,导致最终建立 TLS 连接时间变得更长。
这样,在开始正式开始加载页面,客户端也需要进行一次 HTTP 请求去查询 OCSP。OCSP 也会十分影响页面加载速度,也同样会增加加载页面出错的可能。而 OCSP 有了 OCSP Stapling,这样的话 Web 服务器会预先从 CA 获取好 OCSP 的内容,在与 Web 服务器进行 HTTPS 连接时,这个内容直接返回给客户端。由于 OCSP 内容包含了签名,Web 服务器是无法造假的,所以这一整个过程是安全的。同理,TLSA 记录也可以被预存储在 Web 服务器中,在 TLS 握手阶段直接发送给客户端。这就是 DNSSEC/DANE Chain Stapling,这个想法已经被很多人提出,然而直至现在还未被列入规范。也许浏览器未来会支持 TLSA,但至少还需要很长一段时间。

传统 CA 机制所特有的优势

传统的证书配合了现在的 Certificate Transparency,即使不需要 TLSA 记录,也能一定程度上保证了证书签发的可靠性。此外,传统证书也可以使用 TLSA,其本身的安全性不比 DANE 自签名差。 此外,传统 CA 签发的证书还有自签名证书做不到的地方:
  • OV 以及 EV 证书:DANE 自签名的证书其实就相当于 CA 签发的 DV 证书,只要是域名所有者,就能够拥有这种证书。然而很多 CA 同时还签发 OV 和 EV 证书,OV 证书可以在证书内看到证书颁发给的组织名称,EV 则是更突出的显示在浏览器上:在 Chrome 的地址栏左侧和 HTTPS 绿锁的右侧显示组织名称;Safari 则甚至直接不显示域名而直接显示组织名称。OV 和 EV 的特点就是,你甚至都不用去思考这个域名到底是不是某个组织的,而直接看证书就能保证域名的所有者。而 DV 证书可能需要通过可靠的途径验证一下这个域名到底是不是某个组织的。比如在你找一家企业官网的时候,有可能会碰到冒牌域名的网站,而你不能通过网站是否使用了 HTTPS 而判断网站是否是冒牌的——因为冒牌的网站也可以拿到 DV 证书。而当你发现这个企业使用了 OV 或 EV 证书后,你就不用再去考虑域名是否正确,因为冒牌的网站拿不到 OV 或 EV 证书。在申请 OV 或 EV 证书时,需要向 CA 提交来自组织的申请来验证组织名称,而由于 DANE 证书是自签发的,自然也不能有 OV 或 EV 的效果。
  • IP 证书:CA 可以给 IP 颁发证书(尽管这很罕见),这样就可以直接访问 HTTPS 的 IP。而 DANE 基于域名系统,所以不能实现这一点

CAA - 证书颁发相关

CAA 比较简单,它相当于是公开声明这个域名允许了哪些证书颁发商颁发的证书。CAA 不需要 DNSSEC,写在这里只是和 TLSA 区分一下,但开启 DNSSEC 无疑能够使 CAA 更可信。 比如在域名添加了如下记录,就相当于限制了仅允许 Let’s Encrypt 签发证书,其他证书签发商则不再给这个域名签发证书。在 CAA 记录刚推出时受到以下因素限制:
  • 只有在使用支持 CAA 的证书颁发商,添加 CAA 才有意义
  • 证书颁发商并没有必要 CAA,也就是说即是添加了 CAA 记录后仍可能可以被其他你没有允许的证书颁发商颁发。所以这是需要其他证书颁发商自觉地不再给这个域名办法证书。
但是从 2017 年底开始,所有证书颁发商被强制要求遵守域名的 CAA 记录。来源 CAA 的副作用最小,你也可以添加多个 CAA 记录来允许多个证书颁发商去签发,以增加灵活性。 google.com 就使用了 CAA(但是没有开启 DNSSEC):
$ dig google.com caa
;; ANSWER SECTION:
google.com.86399INCAA0 issue "symantec.com"
如果要将一个域名绑定在某个证书颁发商下,建议同时使用 TLSA 和 CAA。如果是长期的绑定,可以考虑一下 HPKP Pin。

SSHFP - 认证主机相关

在使用 SSH 首次连接一个主机时,SSH 通常会询问是否信任服务器的指纹(fingerprint):
$ ssh guozeyu.com
The authenticity of host 'guozeyu.com (104.199.138.99)' can't be established.
ECDSA key fingerprint is SHA256:TDDVGvTLUIYr6NTZQbXITcr7mjI5eGCpqWFpXjJLUkA.
Are you sure you want to continue connecting (yes/no)?
这个指纹告诉你了你连接到的是这个服务器,而不是别的服务器。你需要确认这个指纹与自己所要连接到的服务器匹配时,才应该进行连接,不然你就会连接到别的服务器(攻击者可以让服务器允许任何来源的 SSH,于是你就成功登陆到了攻击者的服务器。这样你所执行的代码,甚至是通过 SSH 提交的 Git,都能被获取到)。 那些允许公开的 SSH 连接的服务器,如 GitHub,会在网站上公开自己的指纹,用户需到在它们的官方文档中找到指纹。而 SSHFP 则是一种更简单且通用的的公开这个指纹的方式,这个功能甚至都集成到了 SSH 中去,当检测到指纹匹配时就会自动登陆,跳过询问指纹的步骤。 然而,假如攻击者同时控制了网络中的 DNS 和 SSH,那么 SSHFP 反而是更加不安全的。所以客户端仅应该信任使用了 DNSSEC 的 SSHFP 记录。 编辑 ~/.ssh/config ,添加这一行即可实现验证 SSHFP 记录:
VerifyHostKeyDNS=ask
如果将 ask 换为 yes,那么会默认信任 SSHFP 记录。

参考资料

具体实现

前面我们介绍了 DNSSEC 的基本原理,这一篇文章中将会介绍给你 DNSSEC 的具体实现方法,我来使用 dig 程序为大家分析 DNSSEC 的实现过程

根域名

我有一个域名 tlo.xyz 长期部署了 DNSSEC,所以本文就拿这个域名作为例子讲解。首先,需要明确的是如何让 dig 程序去显示关于 DNSSEC 的资源类型,幸运的是这很简单,只需要加上 +dnssec 参数即可。 在之前的文章中,我们已经知道了根域名公开了 13 个服务器的 IP 地址。此外,其实根域名还公开了一组 DS 记录,这段记录可以在这里获得
.172800INDS19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
.172800INDS20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D
  • DS 记录:DNSSEC Delegation Signer,用于鉴定 DNSSEC 已授权_区域的签名密钥(Zone-signing-key)_,相当于公钥的哈希值
第二条记录是 2016 年 10 月份生成,并在 2018 年 10 月完成切换。 现在我们利用这个地址查询根域名自身,结果如下(部分无关内容已经被删掉):
$ dig @a.root-servers.net. . any +dnssec
;; ANSWER SECTION:
.518400INNSa.root-servers.net.
;; 这里省略剩下 12 个根域名服务器
.518400INRRSIGNS 8 0 518400 20170227170000 20170214160000 61045 . HnSVXyC8UZuXnpOsZOv1/GP2byJFG9Y9ch4q0eUw/6CMEJ403spJ67Oo JiAGhdiE6xlONAMQN0Q7LpA7/bgCf29mmVJDcG76b/qaVnmRjKErBwep 68K831Uph2V+Rixcw8mx5XYWuMDyKDiRWlrPyY/bT0a7Us7dTnhkNJ+D g25E0lqXNKY9XgroVoTlwc5tCIe6L8GhoDU+LTLtBySBgQa3kEAI7WUQ CT4l47BCu3zzh8sJtdKGEXnXD0e22pB4ZaYF80iVWL1cRgghn2HphlN0 1kFJr3WuuIKP9r4vZFIjKiinV1KJdBBW2fciGAx+nZbP5sSUlOdiz/56 BZKM3g==
.86400INNSECaaa. NS SOA RRSIG NSEC DNSKEY
.86400INRRSIGNSEC 8 0 86400 20170227170000 20170214160000 61045 . JQQEDSGFolKu38MmdvvDj7Zi2AstqZc2cwhPQE+RRwTBVl3SWQOQ4FaS Wta+CdbhbaRAKQ9dUiOif95LLarewJDF9e4O2zTDsLt5MlgXLGZr3xd4 9HhDkEzjRk4Zro2qquvWmsHUjn+fbru4FsO6sZyS/FWjfh0XImlIYfh4 D50IplgRwv6awu4mO2RzJ0VL94l4WMMnV42vPSfWiNpL+9g7PHmaWkwe EqH7RamPDzw/M3bmts5yWp+cEI4IzE25kmZAHwN9EQHNNtDL3qKtAzrY wj6e8VVw0rI/XJ3DMI5aRk3xB+ac13dQv8cWtQZRImw76A5/N6clBXJS ZpmT+w==
.172800INDNSKEY256 3 8 AwEAAYvgWbYkpeGgdPKaKTJU3Us4YSTRgy7+dzvfArIhi2tKoZ/WR1Df w883SOU6Uw7tpVRkLarN0oIMK/xbOBD1DcXnyfElBwKsz4sVVWmfyr/x +igD/UjrcJ5zEBUrUmVtHyjar7ccaVc1/3ntkhZjI1hcungAlOhPhHlk MeX+5Azx6GdX//An5OgrdyH3o/JmOPMDX1mt806JI/hf0EwAp1pBwo5e 8SrSuR1tD3sgNjr6IzCdrKSgqi92z49zcdis3EaY199WFW60DCS7ydu+ +T5Xa+GyOw1quagwf/JUC/mEpeBQYWrnpkBbpDB3sy4+P2i8iCvavehb RyVm9U0MlIc=
.172800INDNSKEY257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0=
.172800INRRSIGDNSKEY 8 0 172800 20170303000000 20170210000000 19036 . KHz7GVvg5DxUv70bUhSjRy1JO5soL+h6M08g8bSKecd+4NmZI87Sn20p uZNRuiASbnG63i89Z2S45NBAR8KtqB6N5CrRhLhfxZcRo5k3Ts6zsC1E J58upPKzFtu/sJBsPDjcRJJKbXlB4hLukQwVhn/MbsXxZdZGI57WoLFx bbR49NlFJrlrbTi2gieRR1SCLfT9aiBGsJA3T4jXap9FIsikNf1DJA8H cnQTW7hFi8l/O2ni2hbjsIE4S3GRTMypqDR/s7piy/qukfWwSknk6YZT bzld6ZgbZK+oOhRgj/W6XW78bJl0onov0F1wD0NQsec+sk2P+JNMc4xg vQmn9g==
.86400INSOAa.root-servers.net. nstld.verisign-grs.com. 2017021401 1800 900 604800 86400
.86400INRRSIGSOA 8 0 86400 20170227170000 20170214160000 61045 . A5CqIucYyfFTzp03EuajDjp5Vw6dd3Oxip60AI7MCs/2xfBu1red4ZvF GfIEGHstG61iAxf7S3WlycHX9xKyfIOUPmMxuvkI9/NXMUHuvjUjv9KW TTkc1HV6PuUB1sv9gsuQ6GFnHCXAgMKXZs9YofRDlBi2jxAvJVc5U7nG sd8UqQs4WcinMHNvFV9+gwfax0Cr9KFDmDUbS+S2wYmNs+SGOn+CbFrD 8gs34GiYao8i0QGw7RVGTVJiuVOuUkeYe4iSXnJjNjeIlm8liq6PRXgM nI+ndPDogA/a8JATfyzQ97VDRwe/FucoTbe5qd2cHxqh1ZxxPkA3K3Fj 8Jv3kg==
;; ADDITIONAL SECTION:
a.root-servers.net.518400INA198.41.0.4
a.root-servers.net.518400INAAAA2001:503:ba3e::2:30
;; 这里省略剩下 12 个根域名服务器
可以看到,此时没有再返回 DS 记录,因为 DS 记录总是由一个区域的上一级区域的权威服务器返回,之后还会再次提到这个问题。此处的 DNSKEY、RRSIG、NSEC 是三个关于 DNSSEC 的记录类型:
  • DNSKEY 记录:用于 DNSSEC 的记录,内容是一个公钥
  • NSEC 和 NSEC3 记录:用于说明该域名下有哪些记录,从而可以用排除法证明该域名下没有哪些记录。
  • RRSIG 记录:记录集的数字签名,相当于是使用私钥加密后的内容。用于给除去自身外所有的记录集签名。下文有些地方直接将此记录叫做了签名。
可以看到,上方查询的 DNSKEY 记录有两条,这两条的内容的第一项分别是 256 和 257。256 是 ZSK(zone-signing keys),257 是 KSK(key-signing keys)。其中 KSK 是专门用于签名 DNSKEY 记录集(就是 ZSK 与 KSK),而 ZSK 是用于签名该区域下的其他记录集。 仔细观察,就可以看出,每一种记录后面就对应着一个用于签名该种类记录的 RRSIG 记录,比如上面查询结果中的 NS、NSEC、DNSKEY、SOA 记录的后面都跟着一个 RRSIG 记录。 举个例子,客户端解析并验证根域名的 SOA 记录的方法大概如下:
  1. 解析A:使用根域名服务器解析根域名自身下的 DNSKEY 和 SOA 记录,并要求返回签名
  2. 验证1:使用已知的 DS 记录验证 DNSKEY 中的 KSK
  3. 验证2:使用 KSK 及其签名验证 DNSKEY 记录集
  4. 验证3:使用 ZSK 和 SOA 的签名验证 SOA 记录
但是值得注意的是,根域名服务器所返回的 Glue 记录却没有数字签名,那是因为这是不必要的。就算 Glue 记录被篡改成了别的服务器,那个服务器在解析根域名时也不能篡改任何权威记录(在 ANSWER SECTION 下)。

一级域名和二级域名

然后,我们来使用根域名服务器解析一级域名:
$ dig @a.root-servers.net. xyz. any +dnssec
;; AUTHORITY SECTION:
xyz.172800INNSx.nic.xyz.
;; 这里省略剩下 3 个服务器
xyz.86400INDS3599 8 2 B9733869BC84C86BB59D102BA5DA6B27B2088552332A39DCD54BC4E8 D66B0499
xyz.86400INDS3599 8 1 3FA3B264F45DB5F38BEDEAF1A88B76AA318C2C7F
xyz.86400INRRSIGDS 8 1 86400 20170227170000 20170214160000 61045 . gXpaapTu67jlkfOeujL455lFDGLmLkFpnI+f8VNLMehozA7qWQD71oso SXJxkOB6o/ldXqoLGIo1khsYS8SMltOCMisJ6eA2cLokB7Ybzsaw8GWZ rkx64u2JbELWMbwGnY3PnZHGlBT77oAt49KNDfpxhgm3k1Yrcrua25D8 PL4fz6IQYQIMXWiHM/V2jH6E2Vu1Ynrjiu0lPEMf0TnGsK/URnCGE9uZ caT41mNz9kri/wkuQR11XtHjsN/qZgmcxZK+Tf4VQfOOdcfey4wAa1CM HRQ3Pm4mLo4LQwiESeMuqFyriizdMG4piNP7NLuI54GqWCGNSyDYbOdL X0n2Aw==
;; ADDITIONAL SECTION:
x.nic.xyz.172800INA194.169.218.42
x.nic.xyz.172800INAAAA2001:67c:13cc::1:42
;; 这里省略剩下 3 个服务器
这里返回的 DS 记录,虽然是两个,但是其 Key Tag(即第一项,为 3599)是相同的,后面两项算法有所不同,这其实就是同一个 KSK 的两种不同的哈希值算法,这个是为了提高兼容性和有限的安全性。这与刚才根域名的情况不一样,根域名下面的是完全两个不同的 KSK 的不同的 DS。 此时我们发现了不仅是 Glue 记录,NS 记录下也没有签名,这是因为这里返回的 NS 记录是属于委托记录(在 AUTHORITY SECTION 下),也不需要签名,xyz. 下 NS 记录的签名应该有 xyz. 的解析服务器来完成(而 DS 记录是例外)。我们来使用一级域名服务器来解析其自身:
$ dig @x.nic.xyz. xyz. any +dnssec
;; ANSWER SECTION:
xyz.172800INNSx.nic.xyz.
xyz.172800INNSy.nic.xyz.
xyz.172800INNSz.nic.xyz.
xyz.172800INNSgenerationxyz.nic.xyz.
xyz.172800INRRSIGNS 8 1 172800 20170525152637 20170425162314 47496 xyz. p57paKPWMyhwmz5IkkbZOMC/dIfxyANZ6QzRbEBiOff5JXnrdpKEX4YT zPMzF4SSNHPuK53uuJTtt2E4W3Xd2VjGVUx7V2mP7Hxs0nQblCDbQa51 zr6kYoXEOcdwVx23GyLe0baPELtEkQZHeKx5eWyZTUDCri4DBCZZv9m+ Lbk=
xyz.3600INSOAns0.centralnic.net. hostmaster.centralnic.net. 3000288446 900 1800 6048000 3600
xyz.3600INRRSIGSOA 8 1 3600 20170606000517 20170506113911 47496 xyz. oanexcZRLZ+NEPvSGhl0qyi6LH/3ubP+0JWjlNvcduZWUp7oQt4VWfy/ w0T2Y2/610u7mvcxRty2p6cZq1arVMLOci7ZzMpPHkNDxXHcNRxlMNL1 6mwLgKzOlxp0acEGhqQBhj/XQ2icScf8PMChC7uRsFOz9nqAxelcgJgY D9I=
xyz.3600INDNSKEY257 3 8 AwEAAbYRTzkgLg4oxcFb/+oFQMvluEut45siTtLiNL7t5Fim/ZnYhkxa l6TiCUywnfgiycJyneNmtC/3eoTcz5dlrlRB5dwDehcqiZoFiqjaXGHc ykHGFBDynD0/sRcEAQL+bLMv2qA+o2L7pDPHbCGJVXlUq57oTWfS4esb GDIa+1Bs8gDVMGUZcbRmeeKkc/MH2Oq1ApE5EKjH0ZRvYWS6afsWyvlX D2NXDthS5LltVKqqjhi6dy2O02stOt41z1qwfRlU89b3HXfDghlJ/L33 DE+OcTyK0yRJ+ay4WpBgQJL8GDFKz1hnR2lOjYXLttJD7aHfcYyVO6zY sx2aeHI0OYM=
xyz.3600INDNSKEY256 3 8 AwEAAaAxrInKa1BlzuJsfT/gWfrUUH5OP7IJquNOLRU7LVbKwJEv655b kBBbW53wVXmnWJfPxykrMme8a91FFqXTYepvVN5vJe9QuCfiW/C64jSo 0HNXhbSUkV1ZDcy+zgAmMriPm8g5ki7KJ7KRs+YRoL2NwCm5fJVsAchr WalFB4z3
xyz.3600INDNSKEY256 3 8 AwEAAdNAEAD8rebFpKuiLr0BwTNQoECMnfJjiZ54ZCCke208h9eX7ui7 WFFz9hjmvAgIFavN5vVhR5SnDTRvD5iDsMKvefXbnz4Qeu4GILwJuTqC QAcqw6RUp1+U1KEkwRP/noqA4fSkmnInbQwW+Yq+bxohGQVatZiAiO/G ppSggZX3
xyz.3600INRRSIGDNSKEY 8 1 3600 20170520002252 20170419140553 3599 xyz. h5TV5pu/QAAUal72x8Dm8tgqBzRvDSznaDrRqV0Fu8ponhfXQFjdG3p1 2/IVdkNLtLZq4I2aUMwJeTZcyq5gRcWOror0V6uChW5fgIkH7abj1CYL tSRv3M7mVBduGNIzMuITJu5Pn1BVXiF9FsTw1ks+wDjdPn2OLe5BKRmj d+6GgwwBhg4V2efFcb+peRBCRpk+i3S1dlMyILCCgAvnAaGbh3k+vaKN 2wb528jSvH0QVIXP8PTAxLw86IfFlvLm8Lxo1e8hweI+4hgECNX7UzeG epXE+LpOiZwkhf7JncytOcxw6YzSAQETYJfcK1MlMcH5zNzjhFTNoMV3 M4QTLQ==
;; ADDITIONAL SECTION:
x.nic.xyz.172800INA194.169.218.42
y.nic.xyz.172800INA185.24.64.42
z.nic.xyz.172800INA212.18.248.42
generationxyz.nic.xyz.172800INA212.18.249.42
x.nic.xyz.172800INAAAA2001:67c:13cc::1:42
y.nic.xyz.172800INAAAA2a04:2b00:13cc::1:42
z.nic.xyz.172800INAAAA2a04:2b00:13ee::42
generationxyz.nic.xyz.172800INAAAA2a04:2b00:13ff::42
可以看到, xyz. 下的解析服务器就返回了 NS 记录的签名。 然而,xyz. 下却有两个 ZSK,这大概是因为 xyz. 下有两个私钥,这样的话每一个签名可以使用两个私钥中的任何一个签,灵活性更高。此外,我们也看出来了区分 KSK 和 ZSK 的意义:KSK 和 ZSK 的数量可以不相等。 然后,我们来使用一级域名的服务器来解析我的二级域名:
$ dig @x.nic.xyz. tlo.xyz. any +dnssec
;; AUTHORITY SECTION:
tlo.xyz.3600INNSkami.ns.cloudflare.com.
tlo.xyz.3600INNSgordon.ns.cloudflare.com.
tlo.xyz.3600INDS2371 13 2 913F95FD6716594B19E68352D6A76002FDFA595BB6DF5CAAE7E671EE 028EF346
tlo.xyz.3600INRRSIGDS 8 2 3600 20170303035908 20170201045318 7558 xyz. b69lhRaZM8lWN44qaQCCm4+479ATwt+OlRWD770jmLJnai2ob/0CWPEZ pFQ+y/k6n/X8VPZa2IVwxB6qUTtirtOolBHVA4gmPQffXiYiTbP1dDT9 G7BwNMdOCGkH0bySW9rFpi3zKYvOieNQLlV/i61ox78AgxQeX4k800QN gEE=
由于 NS 记录属于委托记录,所以 NS 下也没有签名。 由于这个域名使用的 NS 是 kami.ns.cloudflare.com. ,不属于 xyz. 之下,所以没有任何 Glue 记录,于是这需要再按照流程再重头解析一遍 kami.ns.cloudflare.com. ,这里就省略了。 最后,我们来使用我二级域名服务器来解析二级域名自身:
$ dig @kami.ns.cloudflare.com. tlo.xyz. any +dnssec
;; ANSWER SECTION:
tlo.xyz.60INA52.84.21.12
tlo.xyz.60INA52.84.21.243
tlo.xyz.60INA52.84.21.67
tlo.xyz.60INA52.84.21.107
tlo.xyz.60INA52.84.21.4
tlo.xyz.60INA52.84.21.46
tlo.xyz.60INA52.84.21.29
tlo.xyz.60INA52.84.21.224
tlo.xyz.60INRRSIGA 13 2 60 20170507145606 20170505125606 35273 tlo.xyz. tcMNEbUGrnCoTK1Z7Xmo15k+pLyZJ+m28nKt/o5s+/ezrcMsgFv1C0bY ABs9M8cqjw+0Ld8DTtAwTQVwpAUe+g==
tlo.xyz.60INAAAA2600:9000:203a:1000:b:fe0:fc00:93a1
tlo.xyz.60INAAAA2600:9000:203a:7e00:b:fe0:fc00:93a1
tlo.xyz.60INAAAA2600:9000:203a:e400:b:fe0:fc00:93a1
tlo.xyz.60INAAAA2600:9000:203a:a200:b:fe0:fc00:93a1
tlo.xyz.60INAAAA2600:9000:203a:4800:b:fe0:fc00:93a1
tlo.xyz.60INAAAA2600:9000:203a:3000:b:fe0:fc00:93a1
tlo.xyz.60INAAAA2600:9000:203a:1a00:b:fe0:fc00:93a1
tlo.xyz.60INAAAA2600:9000:203a:a000:b:fe0:fc00:93a1
tlo.xyz.60INRRSIGAAAA 13 2 60 20170507145630 20170505125630 35273 tlo.xyz. QV5gEUO9NK3W2G4aF/dTZrmsGURyVAiU3eyyuR4lp4YJ7jxGjmCQArPB 4CYz6laN+V6Kd78gi7v50gaf+WCeDQ==
tlo.xyz.3600INSOAgordon.ns.cloudflare.com. dns.cloudflare.com. 2024522030 10000 2400 604800 3600
tlo.xyz.3600INRRSIGSOA 13 2 3600 20170507145653 20170505125653 35273 tlo.xyz. KnJkiBfvb0xhw3mAjKxnWPSMptc+eoN7Qh50HJQYnmycvV1K9ADFKYyq RwhKzWEOFHXtsn8Pxh+d/EY0x4EVEw==
tlo.xyz.86400INNSgordon.ns.cloudflare.com.
tlo.xyz.86400INNSkami.ns.cloudflare.com.
tlo.xyz.86400INRRSIGNS 13 2 86400 20170507145712 20170505125712 35273 tlo.xyz. vQDzeIteIeVdbPS7nxNXCVeGD97+ePvEHdPK263oocoDPY59tVOG6V+a s7k8GHSFJ8KKu8edoWcUayi3aNFY7g==
tlo.xyz.86400INTXT"v=spf1 include:email.freshdesk.com include:\_spf.myorderbox.com include:amazonses.com -all"
tlo.xyz.86400INRRSIGTXT 13 2 86400 20170507145729 20170505125729 35273 tlo.xyz. NDFDF9PHFSSvQu7oF17cNWIrQUrfaPA/019i6hCvj7JJiA21DWp0w5J3 BlxDEN6wIGq4Nzb4IVE0uf+zmdTb0w==
tlo.xyz.3600INDNSKEY257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0dxCjjnopKl+GqJxpVXckHAeF+ KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==
tlo.xyz.3600INDNSKEY256 3 13 koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELKTqT+qRGrZpWSccr/lBcrm10Z 1PuQHB3Azhii+sb0PYFkH1ruxLhe5g==
tlo.xyz.3600INRRSIGDNSKEY 13 2 3600 20170529092807 20170330092807 2371 tlo.xyz. SDm3eGWVamR+GIZ8TEcYDeik73gMUVyX6TGGtkir6A6TIY+2zvXwtfrN HEvkygTfiOuEn+/Ipj08o8+NyZeAZw==
下面总结一下解析并验证 tlo.xyz. 下的全部 A 记录的方法,DNS 在实际解析过程中会尝试尽可能跳过不必要的请求:
  1. 解析A:使用根域名服务器解析根域名下的 DNSKEY 记录,并要求签名
  2. 验证1:使用已知的根域名 DS 记录验证根域名的 KSK
  3. 验证2:使用根域名的 KSK 及其签名验证 DNSKEY 记录集
  4. 解析B:使用根域名服务器解析 tlo.xyz. ,返回的是 xyz. 下的 NS 和 DS 记录,包含了签名
  5. 验证3:使用根域名的 ZSK 和 DS 的签名验证 xyz. 的 DS 记录
  6. 解析A:使用 xyz. 服务器解析 xyz. 下的 DNSKEY 记录,并要求签名
  7. 验证1:使用 xyz. 的 DS 记录验证 xyz. 的 KSK
  8. 验证2:使用 xyz. 的 KSK 及其签名验证 DNSKEY 记录集
  9. 解析B:使用 xyz. 服务器解析 tlo.xyz. 下的 NS 和 DS 记录,并要求签名
  10. 验证3:使用 xyz. 的 ZSK 和 DS 的签名验证 tlo.xyz. 的 DS 记录
  11. 解析A:使用 tlo.xyz. 服务器解析 tlo.xyz. 下的 DNSKEY 和 A 记录,并要求签名
  12. 验证1:使用 tlo.xyz. 的 DS 记录验证 tlo.xyz. 的 KSK
  13. 验证2:使用 tlo.xyz. 的 ZSK 和 A 的签名验证 tlo.xyz. 的 A 记录
为了做区分,我把解析分为了两类,验证分为了三类:
  • 解析A:解析权威记录并要求签名
  • 解析B:解析委托记录并要求 DS 记录的签名
  • 验证1:根据 DS 验证 KSK
  • 验证2:根据 KSK 验证 ZSK
  • 验证3:根据 ZSK 验证解析记录

NSEC 记录

NSEC 记录比较特殊,所以单独的讲一下。 在全面普及 DNSSEC 之前,仍然有不少域名并不支持 DNSSEC,此时如何让已经支持 DNSSEC 的网站进行签名认证,拒绝解析签名错误的请求,又同时让没有 DNSSEC 的域名无视签名正常解析呢?HTTPS 的推进是区分了协议:以 https:// 开头的网站进行签名认证,以 http:// 开头的网站不进行签名认证,在 HSTS Preload 里的域名则强制进行签名验证。而实际上,HTTP 和 HTTPS 是两种不同的协议,而支持 DNSSEC 的 DNS 与普通的 DNS 是同一种协议,前者是后者的子集。只有域名下有 DS 记录时,才会进行签名认证,否则还是按照普通的处理。 那么试想,攻击人可以在解析 tlo.xyz. 时的第九步做手脚:删除 DS 记录以及 DS 的签名,这样不就相当于移除了这个域名的 DNSSEC 了吗?(有些类似于 HTTPS 降级攻击),或者直接删除某个域名下的 A 记录,客户端能知道这个域名下是真的没有 A 记录还是被恶意删除了?实际上这样做手脚是没用的,当开启了 DNSSEC 的权威服务器收到了一个不存在的记录的请求时(这可以是不存在的子域名,也可以是某个域名下不存在的一些记录类型),不是返回空的内容,而是返回一个 NSEC 记录去声明这个域名下没有这种记录,同时也将这个记录签名。综上所述,开启了 DNSSEC 后对于该区域下的所有的 DNS 请求都会签名,从来不会返回空的内容。 根据这里公开的数据,我们来尝试一下解析第一个不支持 DNSSEC 的一级域名:ae. 的 DS 记录的结果
$ dig @a.root-servers.net. ae. ds +dnssec
;; AUTHORITY SECTION:
.86400INSOAa.root-servers.net. nstld.verisign-grs.com. 2017021401 1800 900 604800 86400
.86400INRRSIGSOA 8 0 86400 20170227170000 20170214160000 61045 . A5CqIucYyfFTzp03EuajDjp5Vw6dd3Oxip60AI7MCs/2xfBu1red4ZvF GfIEGHstG61iAxf7S3WlycHX9xKyfIOUPmMxuvkI9/NXMUHuvjUjv9KW TTkc1HV6PuUB1sv9gsuQ6GFnHCXAgMKXZs9YofRDlBi2jxAvJVc5U7nG sd8UqQs4WcinMHNvFV9+gwfax0Cr9KFDmDUbS+S2wYmNs+SGOn+CbFrD 8gs34GiYao8i0QGw7RVGTVJiuVOuUkeYe4iSXnJjNjeIlm8liq6PRXgM nI+ndPDogA/a8JATfyzQ97VDRwe/FucoTbe5qd2cHxqh1ZxxPkA3K3Fj 8Jv3kg==
ae.86400INNSECaeg. NS RRSIG NSEC
ae.86400INRRSIGNSEC 8 1 86400 20170227170000 20170214160000 61045 . B03J+aJuEA5r5Va8QiecBHZUucisWgdC8b14Q4MU5oGSdgmK9PmHLKMS mUiGj/OzH51P1l0G6zxG6bxU56tZ4gSME+rcpIntdKyiWU4QLpkiPa32 aApHFmu0pzugGSDWnQUmNDmCig7jJ2J61xlOzx19ni0eJazAthRtGWuK WI9bCVt9Yb7Bd21AedC0gugQWY+LKj7HR3zRhZ5dywpcTQUc78BrJDvh P8UxWprUJozcMYdVDqA5TvSlRHz8aLOnkD/olVsE5cU6qSvCX32E7WuQ IeFfhf1J940hly/3f960Dvm5kwX8l6CkNW083yLCnG8e7zArEUBRthvA a90SJw==
注意新增的 NSEC 记录,这个记录首先声明了一级域名 ae. 下只有 NS RRSIG NSEC 这三种记录,也就是说没有 DS 记录。此外,它还说明了 ae. 之后的一个一级域名是 aeg. ,所以通过这个记录,可以轻松的证明不存在 aea. 、aeb. 、aec. …… 这些一级域名。 那么,如果请求 aea. 这个不存在的一级域名,会发生什么情况?
$ dig @a.root-servers.net. aea. any +dnssec
;; AUTHORITY SECTION:
ae.86400INNSECaeg. NS RRSIG NSEC
ae.86400INRRSIGNSEC 8 1 86400 20170228050000 20170215040000 61045 . U2e52sVPmIup4pSfWzg7hupPZb63NdYdsiNEqr2ygDBQrgOQ6rT2SZkP xZVvHc7ZtfggUV1iT6kels8+d3beURz0Vf58x6up+PUF6svaFOmx2Bpu 42owq6wYQH6ll8GLOKiIC/35omIXja0VFj4ueG1HsbHbWVxUcL5bsDrt UWRUU9Hp1ySp36+H7M5NE+YPNk8soH2xyANe+STkymH661m8jJqXbG2X atbCEEOtuXuplvS7Rm/YRS+UEtsamC3A9bDBnus/OiL3KS1ztuvrxQfS 6a1z45UtL0PBBQ5DzNiVd9QHHhSpsaxFUqg0iw21CB6MZaK10EB7EJCQ EWkRkg==
.86400INNSECaaa. NS SOA RRSIG NSEC DNSKEY
.86400INRRSIGNSEC 8 0 86400 20170228050000 20170215040000 61045 . fnA/PW3QvSzI4MXZ+ylGhv/Z+F+u6YdAWnSz1DfbwSZkcpzwZoO1/uiY QtYhYU5GF/dbTk7oGEjStA0dWVzyyf+7opW+DS1+R9pn5N/LynyqZ6Et Swk85MQl04gu5LxLrnn6Nind2ozRMha4Nn7tNlYG59GLH3hXkaQ6xYmE hD0Ya+UE6h2vcQ8Y8m3ccifDO2rBukdsUJ13dZLAScNAVJU6/2YxlyyX fYY7G0Ktqu5Tq10YvfJazZ5VraBzw+bkEzM8UEPGNNfX9FTB7zxhjyhU h1u87Z/nKMoIznzVu6Xk9AC5JM1lU/OIHyYHCp+XzMGuUdjwNZH706ND MGq/rQ==
.86400INSOAa.root-servers.net. nstld.verisign-grs.com. 2017021500 1800 900 604800 86400
.86400INRRSIGSOA 8 0 86400 20170228050000 20170215040000 61045 . Nj7xEVPJ5DtBFRP9Zy0GCIwY/ax3v9n9JV0EsKyAeHPYDw4PBMpXQRxa banAl7DVyytO+xLz1NxY3iYTSPtyFjbAzkipC5BJT0EFovbQJ7VJOS4P nZBFaVltjGnzzrC8+hWESyhcwn2DdsNw94JqlkVPEtbT+u6vgXbIv5lD 1/YJMRcvWR74FzBC/bYyk+s0WWVNWDenioI2F7NCRgKSYm1+6qXK4on7 MFAmJE9TYYyZFFRiQurS1wH+d3/xQTtjd93GYOhpWVND0NyN/t4nkxhT spHrofo9GvzTIcGTcwT4Pp1bdBXL6dS0P+JIDXTKQN7u/3RwoJj/6jPm FOSEKw==
通过请求 aea. ,从请求的结果可以得出两个结论:
  1. ae. 和 aeg. 是两个相邻的一级域名(这也就相当于声明了不存在 aea. 这个一级域名),此外也说明了 ae. 下只有 NS SOA RRSIG NSEC DNSKEY 这些记录。
  2. 根域名之后的一个一级域名是 aaa. ,此外也说明了根域名下只有 NS SOA RRSIG NSEC DNSKEY 这些记录。
注意,相邻的域名排序是包含该区域所有子域名的,也就是说所有的子域名都参加到了排序,刚才得出的 “ae. 和 aeg. 是两个相邻的一级域名” 其实并不准确,而应该是根域名区域下 ae. 和 aeg. 是两个相邻的子域名,因为 NSEC 结果还相当于说明了 a.ae. 这样的二级域名在根域名区域下也不存在。 如果 NSEC 是最后一个记录,那么它的下一个就又是区域自身了。 然而,可以发现,通过这个方法可以轻松的获得已经存在的记录:比如我只是试了一下 aea. 这个一级域名,却一下子就知道了根域名下还有 aaa. 、ae. 和 aeg. 三个一级域名,通过这样一直往下遍历,就能搜索到一个区域下的所有子域名。 知道所有的一级域名对于根域名服务器无所谓,因为一级域名的列表本来就是公开的。可是,这个功能也许不是我们所期望的,有的时候,想要在自己的域名下放置一些只有自己知道的子域名,这些子域名也许就是自己源站服务器的 IP,如果 DNSSEC 就这样实现的话,这就让其他人很容易就能遍历出来你所有的子域名。所以,在 DNSSEC 中,响应记录不存在的话还有两种解决方案,一种是对 NSEC 的 Hack,还有一种是 NSEC3:

NSEC 的 Hack

Cloudflare 上就使用了对于 NSEC 的 Hack,这样就能避免其他人遍历你的所有子域名。举个例子,正常的 NSEC 去解析 a.tlo.xyz 可能是这个结果(假如我只有 www 这一个子域名):
$ dig @kami.ns.cloudflare.com. x.tlo.xyz. +dnssec
;; AUTHORITY SECTION:
tlo.xyz.3600INNSECwww.tlo.xyz. A MX TXT AAAA DNSKEY RRSIG NSEC
; 省略一个 RRSIG 记录
www.tlo.xyz.3600INNSECtlo.xyz. CNAME RRSIG NSEC
; 省略一个 RRSIG 记录
; 还省略了一个 SOA 以及 SOARRSIG 记录
通过观察 NSEC 记录,就可以直接看出这个域名下只有 www 一个子域名。 然而 Cloudflare 实际返回的结果是这样的:
$ dig @kami.ns.cloudflare.com. x.tlo.xyz. +dnssec
;; AUTHORITY SECTION:
tlo.xyz.3600INSOAgordon.ns.cloudflare.com. dns.cloudflare.com. 2023791623 10000 2400 604800 3600
tlo.xyz.3600INRRSIGSOA 13 2 3600 20170216150135 20170214130135 35273 tlo.xyz. ARUYgesljY5azg1RqFgoKbTN6OOmAQUqTsLiQyTBXAMO4P/CecFGwTKY f1cVTI/s4euNahfGOvc0MnDb2R55TQ==
x.tlo.xyz.3600INNSEC\\000.x.tlo.xyz. RRSIG NSEC
x.tlo.xyz.3600INRRSIGNSEC 13 3 3600 20170216150135 20170214130135 35273 tlo.xyz. y4g0Of3Ir/DqcbRT1ND5kwdGXlW++Zb+c9Cx0z60UAzbI+cpW2DDOmBB 4MMKi4zV9xEBg5Jq/8hwBGVo4ytDDg==
Cloudflare 这样则是告知了 x.tlo.xyz. 是存在的,但是只有 RRSIG 和 NSEC 记录,即相当于这个域名下没有任何记录。x.tlo.xyz. 之后的下一个域名是 \000.x.tlo.xyz. ,而实际上那个域名也是不存在的。这其实相当于 Cloudflare 撒了一个谎,并没有直接告知你这个域名的下一个域名。这虽然解决了问题,但是并不符合规范。

NSEC3

NSEC3 使用了在区域内的下一个记录内容的哈希值(按照哈希值的顺序排序)代替了原本的记录内容。从哈希值反推记录内容本身有一定的难度,于是就能够避免其他人遍历出所有的记录内容。(guozeyu.com 没有在生产环境中开启 DNSSEC,以下内容仅为测试结果)
$ dig @a.ns.guozeyu.com. ns.guozeyu.com +dnssec
;; AUTHORITY SECTION:
guozeyu.com.3600INSOAa.ns.guozeyu.com. ze3kr.icloud.com. 1 21600 3600 1209600 3600
guozeyu.com.3600INRRSIGSOA 8 2 21600 20170411191942 20170403191942 52311 guozeyu.com. bHSh4a0zcaFEwS5dNEj/JT9Aosuy8Wdh+U2WaPou95iywqG6VhH85BXT EhYnjmeph/CABF5HC2OvUf9HhcnxjPF9NAQ2cfPTr6Ael9aNBGLFSejI 5VmCdp4Q1sYD6hS51k5BY22bJRyu9v8zWHNLYDRJSFBk4kR0RSV5n0CK 4pA=
67uromrbachidk57be8035jf9gqnhmn1.guozeyu.com. 300IN NSEC3 1 0 1 F327CFB1FFD107F1 ENPCB7U0K7KFHLSCEOTOB7RAHS4TCH3V
67uromrbachidk57be8035jf9gqnhmn1.guozeyu.com. 300IN RRSIG NSEC3 8 3 300 20170411191942 20170403191942 52311 guozeyu.com. MpV+6foWp+XQpwJnNCiIE0dqGigqX+2Z7XWuCFAd/TUS1sBHwnTRKmB5 Rl8Wf23ZMMfZh/oRHQbm4vE1RecMd78ZuvQM61iOmwAOmjIhJJh+LPSg 5KXMmYimTmtyd7/N437XYqmBREbz9EQ66ZqGucOahncPfxX2jhErvICN KDc=

标准的 NSEC 相比 NSEC3 的优点

标准的 NSEC 会暴露所有的子域名,而 NSEC3 不会,看起来 NSEC3 的优势明显。然而标准的 NSEC 相比 NSEC3 又有好处:子(Slave)DNS 服务器不需要拥有 DNS 的私钥,这样配置 Slave DNS 后就方便多了,和常规的 Slave 一样,只需要传送(Transer)整个区域即可,也能够正确的响应不存在的子域名。因为在标准的 NSEC 下 NSEC 和 RRSIG 的数量是有限的。而 NSEC3 或者 Hacked NSEC 都会根据不同的子域名返回不同的 NSEC(3) 记录,NSEC(3) 和 RRSIG 记录都是无限的。 举个例子,比如你现在可以下载到签名过的根域名的区域。其中包含了所有的 NSEC 记录,这样 RRSIG 可以在一台机器上生成,并将签名过的整个区域传送给其它子的根域名服务器上,这样能够有效的确保私钥安全。而用 NSEC3 或者 Hacked NSEC 的话,每一个子 DNS 服务器都需要有私钥。根域名服务器的数量众多,也由各种不同的组织管理着,所以很有必要保护好私钥。所以对于这种不怕被遍历到所有子域名的区域来说,使用标准的 NSEC 也未尝不可。

from https://archive.is/a2Uqs#selection-741.1-3377.138