“科学上机”系列之 DNS 篇

信息安全

#1

使用 DNSCrypt 协议抵挡 DNS 污染

至少从 5 月 2 日 23 时起,www.mlzs.work 的 DNS 域名解析受到严重干扰,甚至 DNSSEC 都无法抵御。使得未在 DNS 上做任何防御措施者无法访问论坛。应某位同志的邀请,我在此写写我是如何抵御 DNS 污染的。

DNS 简介

DNS,全称 Domain Name System,中译域名系统,其主要功能为将人类可读、易于记忆的域名对应到真正用于通信的互联网地址(即 IP 地址)及相应扩展信息并提供查询它们的服务。

实际上,hosts文件正是在域名系统诞生前解决域名解析问题的方法,在域名系统诞生后仍作为最高优先保留:系统遇到域名首先尝试通过hosts文件解析,如能查到对应地址则直接返回之,否则才通过 DNS 继续查询——这正是通过手动将论坛所在服务器的 IP 地址和域名的对应关系依格式填入hosts文件可作为应对 DNS 污染的紧急措施的原因。

DNS 使用客户端-服务器架构。客户端是集成于如今大部分能够访问互联网的计算机中的 DNS 解析器;而服务器分为两类:真正用于维护域名和 IP 地址对应关系的权威服务器 (Authoritative Server),和位于解析器和权威服务器之间、主要用于向权威服务器转发来自客户端的请求和向客户端缓存来自权威服务器的查询结果的解析服务器 (Resolving Server)

域名和 IP 地址的对应关系是分级存储在大量不同的权威 DNS 服务器上的,因此为了获取一个完整的域名对应的地址,需要按照分级关系依次查询多个权威 DNS 服务器。但是,如今大部分操作系统中的DNS 解析器自身并没有直接执行这种递归查询的能力,而不得不把这项任务委托给有这种能力的解析服务器来执行(一般在客户端侧配置的“DNS 服务器”都属于解析服务器)。其“好处”是一定程度上降低了设计解析器的难度,但此举使得解析器的用户不得不盲信他使用的解析服务器为他提供的是来自权威服务器的未经篡改的结果——这实际上包括两方面:

  1. 解析服务器未主动篡改得到结果;
  2. 解析服务器得到的结果未经(他人)篡改
    ——但如下文所示,在很多情况下,这种盲信是靠不住的。

DNS 的问题

和很多在互联网历史的极早期设计的协议一样,DNS 对(用户的)安全性的考虑极其缺乏,既没有机密性——监听者可以知道用户想解析什么域名;也没有完整性——监听者可以伪造发给用户的解析结果而用户无法鉴别;而这正是我们遇到的 DNS 污染能够成立的原因。

这使得 DNS 成为明网访问的薄弱环节。现在任何为 DNS 增加安全性(本文中“安全性”的主体一律为用户,以下不再重复)的改进都推广得极为缓慢,已经到了世界性难题的程度了。与此相反,主要使用 TCP 的传输一侧的安全性改进推广得很好。(很容易想到其背后有政治考量——资产阶级政府不愿放弃通过 DNS 监视国内用户访问活动的这条途径,尤其是在传输一侧的安全性不断改进的今天。)

现在最常见的 DNS 安全性改进就是仅仅提供了完整性校验的 DNSSEC,但它一来不提供机密性,二来它只能去伪却不能存真(DNS 污染一般只能保证假结果在真结果之前到达,而现在的 DNS 解析服务器都是收到一个结果便停止接收,忽略后面的其他结果;而 DNSSEC 能做到的也只是鉴别收到的结果是真是假,避免解析服务器的缓存被假结果污染,而无法做到忽略假结果收下真结果。笔者曾经因为误会了这一点而尝试过在自己的机器上使用自由软件架设支持 DNSSEC 的递归解析服务器,试图“防御”DNS 污染,结果在这一次的干扰事件中证实了 DNSSEC 能去伪不能存真)。而如上所述,就算是这样的改进,接受它的权威服务器也十分有限。

一个真正能彻底解决 DNS 所有安全性弱点的改进方案——DNSCurve已经面世,它为 DNS 提供了机密性方面的改进,且实现了去伪存真的功能。一旦 DNSCurve 在权威服务器体系内得到广泛部署,我们可以通过在本机架设支持 DNSCurve 的递归解析服务器来彻底杜绝 DNS 污染问题。但由于上文指出过的原因,我们不能指望在短期内该改进会在权威服务器间得到广泛支持。

尽管如此,仍有人利用稍作修改的 DNSCurve 的加密通信协议实现了用于在 DNS 解析器和解析服务器之间提供加密保护的 DNSCrypt。尽管我们仍然不得不“盲信”提供 DNSCrypt 支持的解析服务器,它仍然是在 DNSCurve 得到大规模部署前我们能用于抵抗 DNS 污染的最佳方案。

DNSCrypt-Proxy 简介

DNSCrypt 在用户侧最主要的实现是DNSCrypt-Proxy(以下简称解析代理)。它运行于用户的计算机上,表现为一个 DNS 解析服务器,将收到来自解析器的 DNS 请求加密后发送到预先配置好的、支持 DNSCrypt 的真正的上游解析服务器上;并将解析服务器返回的、加密的响应解密后发给解析器。它也像一般的 DNS 缓存服务器那样支持缓存功能。

DNSCrypt 使用的加密通信协议基于椭圆曲线 Curve25519 上的迪菲-赫尔曼密钥交换(其原理在XMPP 通信教程中引用的讲解 OTR 协议原理的文章中介绍过):解析代理向保存于本地的预置信任列表中的解析服务器请求其DNSCrypt 证书(其中包含服务器使用的 DH 像),用信任列表中的公钥验证证书的有效性后,便可生成自己的 DH 源-像对,用自己的源和服务器的像算出共享秘密加密 DNS 请求,和自己的 DH 像一齐发给上游服务器;上游服务器收到加密的请求后,用自己的源和解析代理的像即可算出相同的共享秘密,用来解密请求和加密响应并发回给解析代理。(详情见 https://dnscrypt.info/protocol

在 GNU/Linux 上配置 DNSCrypt-Proxy

笔者使用的 GNU/Linux 发行版直接收录了 DNSCrypt-Proxy,使用包管理器安装好后即监听于127.0.2.1:53。但为了适应中国的网络环境,建议更改以下配置(在笔者的机器上,DNSCrypt-Proxy 的主配置文件位于 /etc/dnscrypt-proxy/dnscrypt-proxy.conf):

ResolverName random
EphemeralKeys on

第一条的含义是在预置信任列表中随机选择一台上游解析服务器,以避免被针对封锁;第二条的含义是为每一次解析生成一个新的 DH 源-像对,以免被跟踪。

DNSCrypt-Proxy 主配置文件的全部选项、含义及默认值请参考此示例:dnscrypt-proxy.conf.gz (3.4 KB)
(M$WIN 用户请在解压缩后使用Notepad++等专业文本编辑器打开)

使用hostip测试(如$ hostip -r 127.0.2.1:53 zh.wikipedia.org),确认它能正常工作后,在你的dhclient.conf(位置可能与发行版有关)中添加一行:

prepend domain-name-servers 127.0.2.1;

并在/etc/default/resolvconf(位置可能与发行版有关)中添加一行

TRUNCATE_NAMESERVER_LIST_AFTER_LOOPBACK_ADDRESS=yes

重新连接网络之后/etc/resolv.conf文件会被更新,机器上的解析器就会根据其中的信息使用监听于127.0.2.1:53的 DNSCrypt-Proxy 做 DNS 查询,DHCP 给出的 DNS 将被屏蔽掉。

如果你需要 DHCP 给出的 DNS,可以在/etc/default/resolvconf中将其设置为no来关闭这一保护,但因前文所述理由这样做会有安全隐患,笔者不推荐关闭这一功能。如果需要对付某些公共无线局域网热点的注册流程,可以将 DHCP 给出的 DNS 信息手动写入resolv.conf,注册成功后再去掉;笔者也在研究更安全的对付注册流程的方法。当然笔者曾见过某些特别恶劣的公共无线局域网热点,其中不指向 DHCP 提供的 DNS 的所有查询都会被屏蔽,遇到这种流氓热点你唯一能做的就是躲着走了。

DNSCrypt 也可部署于运行 OpenWRT 的家庭网关/路由器中,详情见

其他操作系统

M$WIN 用户可以尝试 Simple DNSCrypt,其选项请参考上面给出的完整配置文件示例。

已 root 的 Android 用户则可尝试下 https://github.com/laggardkernel/dnscrypt-proxy-magisk#magisk-version-installation

但是,在尝试上述方案之余,请考虑一下使用更加自由的操作系统,如GNU/LinuxResurrention RemixLineageOS 等自由的非原装 Android 发行版。

因为,只有自由软件才能保护用户,只有自由软件才能解放用户。

全世界无产者,通过自由软件,联合起来!

返回总纲


“科学上机”系列——总纲
#2

以下开放答疑

有关 DNS 的疑问欢迎在此以跟帖形式提出,但提问前请确保自己满足了版规,否则可能遭到以惩前毖后,治救人为目的、以讽刺为主要形式的轻微惩罚。
我们不惯伸手党。

另外如果有同志知道在非 GNU/Linux 的操作系统上有实现类似功能的自由软件也欢迎跟帖指出——因为笔者多半因为用不着而不知道它们。


#3

我记得以前使用过用OPENDNS,当时因为学校强制必须使用校内DNS,其他DNS的UDP流量全部都被屏蔽了,所以为了一些目的用的这个,当时选的是443的TCP接口(目测是over TLS)。虽然解析速度慢了一点,但是由于是TLS流量所以无法屏蔽,不知道这个算不算DNSCrypt的一种?现在因为shadowsocks自带远程DNS解析所以暂时没有这个需求。

然后是自由Android操作系统,其实这个推广起来相比以前要困难很多了。其主要的原因就是现在很多厂商(主要是国产)锁了BootLoader,更有甚者是深度定制。要刷这些系统的话,需要注册账号等一系列将个人信息拱手相让给厂商,于是变相提高了门槛。所以现在的一个需求就是寻求或者探索一种比较简便的干掉这种限制的方法。

另外SIMPLE DNSCRYPT的网站貌似打不开。请确认一下


#4

当时您使用 OPENDNS 在本地安装过什么软件吗?

第二点以后我会专门撰文讲解,不过在这里可以稍微给一点提示:根据软件来选购硬件。

第三点已经修正,感谢指出。


#5

当时使用的就是一个OPENDNS的客户端,我都不知道自己在哪里搞到的。上面有两个选项,一个一般的DNS,也就是通过UDP的,一个是TLS(443端口)。


#6

它明确说过是 TLS 吗?那么有可能是“DNS-over-HTTP/2”方案,不然可能是使用 443 端口通信的非 TLS 实现。


#7

找到了

当年用的是这个


#8

昨晚用win7测试了下楼主说的SIMPLE DNSCRYPT,感觉还不错。下面我总结下大体操作步骤,供其他同志参考:

  • 首先, 打开Simple DNSCrypt,根据自身设备情况,选择下载程序(我下载的是第一个,64位操作系统)。之后进行安装,这期间可能会提示 .NET Framework (注:句点 “.”是该软件名称的一部分,不应省略;“.Net”又作“dotNet”)版本过低,无法安装。此时,请到这里下载安装 .NET Framework 4.6.2。此时,便可以顺利安装SIMPLE DNSCRYPT了。
  • 安装完毕后按照Simple DNSCrypt官网页面给出的截图进行设置,取消选中第三个面板的“Block iPv6”并保存设置。
  • 最后,切回第一个面板,开启service,激活下面的网卡,便可成功绕开DNS污染。

通过此法可以访问很多被墙的网站。