一般情况下,当我们连接到一个陌生的网络环境时,我们会委托由DHCP告知的DNS服务器来做域名解析的工作。但在我看来,这样的做法有着很大的安全隐患。
使用他人提供的DNS服务器,意味着你查询得到的结果是服务器管理员想要给你的结果。由于权威DNS的作用就是给原本没有域名的主机赋予域名(详情可查阅维基百科),因此在一般情况下,相信权威DNS的解析结果并不会有什么问题,除非权威DNS的管理员中出了叛徒:-)
然而非权威DNS就不一样了:它们的作用并不是为主机赋予域名,而仅仅是解析由客户端发来的域名,并给出结果。最常见的非权威DNS是递归DNS,它按如下方式工作:
比如你打算向一个递归DNS查询的IP地址,而该递归DNS尚未缓存相关的所有地址,则递归DNS首先会向根权威DNS查询负责所有.com域名的权威DNS的地址,然后向该权威DNS(暂名为A)查询负责linuxidc.com的下一级权威DNS——B的地址,最后向B查询的地址,将得到的地址返回给客户端。可以看到,这是一个递归式的过程,我们可以使用BIND附带的DNS调试工具dig以$ dig +trace <domain-name>的命令来观测这一过程。
但有些非权威DNS完全可能将服务器管理员设置的另一个地址返回给你,从而达到一些不可告人的目的。其效果和DNS劫持有几分相似,然而在这种攻击场景中并没有人篡改在网络中传输的DNS数据包,而是你使用的DNS返回的信息本身就是有问题的。
即使你直接使用的非权威DNS没有包藏祸心,权威DNS返回给它的查询结果仍然有可能被篡改。尽管DNSSEC提供了基于数字签名的机制来防止篡改,但我们仍然无法知道我们使用的非权威DNS是否使用了DNSSEC。
安全是不能托付给别人的。以上现实情况都指向一个解决方案:在自己的每一台计算机上搭建一个可以做递归查询的非权威DNS服务器,然后让这台计算机上的DNS客户端只使用它来做DNS查询。
这样的自由软件主要有Unbound和BIND,我使用的是历史最悠久的DNS服务器软件——BIND。
BIND的默认配置就是一个递归DNS服务器,最新版的BIND还自带了DNSSEC的支持。这意味着当你打算解析一个途经的各级权威DNS都部署了DNSSEC的域名(比如台湾地区的域名)时,任何篡改手段都会失去作用,并且只要篡改者无法阻挡真正的查询结果到达,你就总能得到正确的结果。
然而DNSSEC尚未完全普及,实际情况常常是最后一级权威DNS(即最终给出你打算解析的域名对应IP地址的权威DNS)没有DNSSEC支持,这样最顽固的DNS缓存投毒攻击仍然会污染我们的递归DNS服务器的缓存。但如果我们知道某些非权威DNS,向它们查询某些特定的被污染域名总能给出正确的结果,我们还可以利用BIND的DNS zone功能(很可惜unbound似乎无此功能),让它遇到这些域名时转到这些防污染DNS上解析。
BIND的主配置文件named.conf一般会用include语句拆分成几个子配置文件分别管理。我们可以增加一个子配置文件专门配置zone,如
include "/etc/bind/named.conf.zones";
然后在这个子配置文件中写入转发规则:
zone "domain.example.com"{
type forward;
forwarders { ipaddr-of-server0; ipaddr-of-server1;...};
};
当然,子配置文件还可以使用include语句进一步拆分。更高级的用法可以参考BIND的man pages。
这样一来,绝大部分域名仍然倚靠部署在本机的非权威DNS进行带缓存的递归查询,避开了不可信的、由他人提供的、参数不明的非权威DNS;而少数污染严重的域名则转交给相应的防污染DNS解析,这些指向特定防污染DNS的zone可以随发现随添加。综合这两种方法可以抵挡常见的大部分与DNS相关的攻击。
如果你改写了BIND的配置文件,建议使用named-checkconf和named-checkzone检查其语法。需要重新启动BIND使配置生效。注意:这些操作一般都需要root权限。
安装BIND并用dig测试,确认它能正常工作后,在你的dhclient.conf(位置可能与发行版有关)中添加一行:
prepend domain-name-servers 127.0.0.1;