目录
一、什么是DNS
二、DNS原理
三、什么是DNS劫持及危害
四、DNS劫持方法
五、如何防止DNS劫持(网络层面)
六、如何防止DNS劫持(应用层面)
七、历史著名劫持案例
检测网站是否被劫持
域名是否被墙
DNS污染检测
网站打开速度检测
网站是否被黑
被入侵
被改标题
被挂黑链 网站劫持检测 远程桌面连接软件
一、什么是DNS
在网络中,机器之间只认识IP地址,机器之间最终都要通过IP来互相访问。但是为了方便记忆,可以为IP地址设置一个对应的域名,通过访问域名,就可以找到对应IP地址的网站。
比如,我们访问今日头条官网的时候,在浏览器地址栏输入头条地址
看似我们访问的是域名,而实际上是通过IP地址访问的今日头条官网。
可以在终端命令窗口ping 今日头条官网域名,就可以看到该域名对应的IP地址了。
从 www.toutiao.com 到 202.108.250.213 的转换工作称为域名解析,域名解析需要由专门的域名解析服务器来完成,DNS就是进行域名解析的服务器(Domain Name System 或Domain Name Service)。
二、DNS原理
DNS 查询时,会先在本地缓存中尝试查找,如果不存在或是记录过期,就继续向 DNS 服务器发起递归查询,这里的 DNS 服务器一般就是运营商的 DNS 服务器。
1、DNS工作原理
第一步:
客户机提出域名解析请求,并将该请求发送给本地的域名服务器。
第二步:
当本地的域名服务器收到请求后,就先查询本地的缓存,如果有该纪录项,则本地的域名服务器就直接把查询的结果(域名对应的IP地址)返回。
第三步:
如果本地的缓存中没有该纪录,则本地域名服务器就直接把请求发给根域名服务器,然后根域名服务器再返回给本地域名服务器一个所查询域(根的子域) 的主域名服务器的地址。
第四步:
本地服务器再向上一步返回的域名服务器发送请求,然后接受请求的服务器查询自己的缓存,如果没有该纪录,则返回相关的下级的域名服务器的地址。
第五步:
重复第四步,直到找到正确的纪录。
第六步:
本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时还将结果返回给客户机。
2、查询过程
虽然只需要返回一个IP地址,但是DNS的查询过程非常复杂,分成多个步骤。
工具软件dig可以显示整个查询过程。
上面的命令会输出六段信息。
第一段是查询参数和统计。
第二段是查询内容。
上面结果表示,查询域名 math.stackexchange.com 的A记录,
A是address的缩写。
第三段是DNS服务器的答复。
上面结果显示,math.stackexchange.com有四个A记录,即四个IP地址。
600是TTL值(Time to live 的缩写),表示缓存时间,即600秒之内不用重新查询。
第四段显示 stackexchange.com的NS记录(Name Server的缩写),即哪些服务器负责管理stackexchange.com的DNS记录。
上面结果显示stackexchange.com共有四条NS记录,即四个域名服务器,向其中任一台查询就能知道stackexchange.com的IP地址是什么。
第五段是上面四个域名服务器的IP地址,这是随着前一段一起返回的。
第六段是DNS服务器的一些传输信息。
上面结果显示,本机的DNS服务器是192.168.1.253,查询端口是53(DNS服务器的默认端口),以及回应长度是305字节。
如果不想看到这么多内容,可以使用+short 参数。
上面命令只返回math.stackexchange.com对应的4个IP地址(即A记录)。
3、DNS服务器
下面我们根据前面这个例子,一步步还原,本机到底怎么得到域名math.stackexchange.com的IP地址。
首先,本机一定要知道DNS服务器的IP地址,否则上不了网。通过DNS服务器,才能知道某个域名的IP地址到底是什么。
DNS服务器的IP地址,有可能是动态的,每次上网时由网关分配,这叫做DHCP机制;也有可能是事先指定的固定地址。Linux系统里面,DNS服务器的IP地址保存在/etc/resolv.conf文件。
上例的DNS服务器是192.168.1.253,这是一个内网地址。有一些公网的DNS服务器,也可以使用,其中最有名的就是Google的8.8.8.8和Level 3的4.2.2.2。
本机只向自己的DNS服务器查询,dig命令有一个@参数,显示向其他DNS服务器查询的结果。
上面命令指定向DNS服务器4.2.2.2查询。
4、域名的层级
DNS服务器怎么会知道每个域名的IP地址呢?
答案是分级查询。
请仔细看前面的例子,每个域名的尾部都多了一个点。
比如,域名math.stackexchange.com显示为math.stackexchange.com. 这不是疏忽,而是所有域名的尾部,实际上都有一个根域名。
举例来说,
www.example.com
真正的域名是
www.example.com.root
简写为www.example.com.。
因为,根域名.root对于所有域名都是一样的,所以平时是省略的。
根域名的下一级,叫做"顶级域名"(top-level domain,缩写为TLD),比如.com、.net;
再下一级叫做"次级域名"(second-level domain,缩写为SLD),比如www.example.com里面的.example,这一级域名是用户可以注册的;
再下一级是主机名(host),比如www.example.com里面的www,又称为"三级域名",这是用户在自己的域里面为服务器分配的名称,是用户可以任意分配的。
总结一下,域名的层级结构如下。
5、根域名服务器
DNS服务器根据域名的层级,进行分级查询。
需要明确的是,每一级域名都有自己的NS记录,NS记录指向该级域名的域名服务器。
这些服务器知道下一级域名的各种记录。
所谓"分级查询",就是从根域名开始,依次查询每一级域名的NS记录,直到查到最终的IP地址,过程大致如下。
-
从"根域名服务器"查到"顶级域名服务器"的NS记录和A记录(IP地址)
-
从"顶级域名服务器"查到"次级域名服务器"的NS记录和A记录(IP地址)
-
从"次级域名服务器"查出"主机名"的IP地址
仔细看上面的过程,你可能发现了,没有提到DNS服务器怎么知道"根域名服务器"的IP地址。
回答是"根域名服务器"的NS记录和IP地址一般是不会变化的,所以内置在DNS服务器里面。
下面是内置的根域名服务器IP地址的一个例子。
上面列表中,列出了根域名(.root)的三条NS记录
A.ROOT-SERVERS.NET、
B.ROOT-SERVERS.NET、
C.ROOT-SERVERS.NET,
以及它们的IP地址(即A记录)
198.41.0.4、
192.228.79.201、
192.33.4.12。
另外,可以看到所有记录的TTL值是3600000秒,相当于1000小时。
也就是说,每1000小时才查询一次根域名服务器的列表。
目前,世界上一共有十三组根域名服务器,
从A.ROOT-SERVERS.NET 到
M.ROOT-SERVERS.NET。
6、分级查询的实例
dig 命令的+trace参数可以显示DNS的整个分级查询过程。
上面命令的第一段列出根域名.的所有NS记录,即所有根域名服务器。
根据内置的根域名服务器IP地址,DNS服务器向所有这些IP地址发出查询请求,询问math.stackexchange.com的顶级域名服务器com的NS记录。
最先回复的根域名服务器将被缓存,以后只向这台服务器发请求。
接着是第二段。
上面结果显示.com域名的13条NS记录,同时返回的还有每一条记录对应的IP地址。
然后,DNS服务器向这些顶级域名服务器发出查询请求,询问math.stackexchange.com 的次级域名stackexchange.com的NS记录。
上面结果显示 stackexchange.com 有四条NS记录,同时返回的还有每一条NS记录对应的IP地址。
然后,DNS服务器向上面这四台NS服务器查询 match.stackexchange.com的主机名。
上面结果显示,match.stackexchange.com有4条A记录,即这四个IP地址都可以访问到网站。
并且还显示,最先返回结果的NS服务器是
ns-463.awsdns-57.com,
IP地址为205.251.193.207。
7、NS 记录的查询
dig命令可以单独查看每一级域名的NS记录。
+short
参数可以显示简化的结果。
8、DNS的记录类型
域名与IP之间的对应关系,称为"记录"(record)。根据使用场景,"记录"可以分成不同的类型(type),前面已经看到了有A记录和NS记录。
常见的DNS记录类型如下。
-
A
地址记录(Address),返回域名指向的IP地址。 -
NS
域名服务器记录(Name Server),返回保存下一级域名信息的服务器地址。该记录只能设置为域名,不能设置为IP地址。 -
MX
邮件记录(Mail eXchange),返回接收电子邮件的服务器地址。 -
CNAME
规范名称记录(Canonical Name),返回另一个域名,即当前查询的域名是另一个域名的跳转,详见下文。 -
PTR
逆向查询记录(Pointer Record),只用于从IP地址查询域名,详见下文。
一般来说,为了服务的安全可靠,至少应该有两条NS 记录,而A记录和MX记录也可以有多条,这样就提供了服务的冗余性,防止出现单点失败。
CNAME
记录主要用于域名的内部跳转,为服务器配置提供灵活性,用户感知不到。
举例来说,
facebook.github.io这个域名就是一个 CNAME记录。
上面结果显示,facebook.github.io的CNAME记录指向github.map.fastly.net。
也就是说,用户查询facebook.github.io的时候,实际上返回的是github.map.fastly.net的IP地址。
这样的好处是,变更服务器IP地址的时候,只要修改github.map.fastly.net这个域名就可以了,用户的facebook.github.io域名不用修改。
由于CNAME记录就是一个替换,所以域名一旦设置CNAME记录以后,就不能再设置其他记录了
(比如A记录和MX记录),这是为了防止产生冲突。
举例来说
foo.com
指向bar.com,而两个域名各有自己的MX记录,如果两者不一致,就会产生问题。由于顶级域名通常要设置MX记录,所以一般不允许用户对顶级域名设置CNAME记录。
PTR
记录用于从IP地址反查域名。dig 命令的-x参数用于查询PTR记录。
上面结果显示,192.30.252.153 这台服务器的域名是 pages.github.com。
逆向查询的一个应用,是可以防止垃圾邮件,即验证发送邮件的IP地址,是否真的有它所声称的域名。
dig
命令可以查看指定的记录类型。
三、什么DNS劫持以及危害
1、什么是DNS劫持
DNS劫持又称域名劫持,是指通过某些手段取得某域名的解析控制权,修改此域名的解析结果,导致对该域名的访问由原IP地址转入到修改后的指定IP,其结果就是对特定的网址不能访问或访问的是假网址。
如果可以冒充域名服务器,然后把查询的IP地址设为攻击者的IP地址,这样的话,用户上网就只能看到攻击者的主页,而不是用户想要取得的网站的主页了,这就是DNS劫持的基本原理。
DNS劫持其实并不是真的“黑掉”了对方的网站,而是冒名顶替、招摇撞骗罢了。
2、DNS劫持危害
-
钓鱼诈骗
网上购物,网上支付有可能会被恶意指向别的网站,更加加大了个人账户泄密的风险。 -
网站内出现恶意广告
-
轻则影响网速,重则不能上网
四、DNS劫持方法
1、利用DNS服务器进行DDOS攻击
正常的DNS服务器递归查询过程可能被利用成DDOS攻击。假设攻击者已知被攻击机器的IP地址,然后攻击者使用该地址作为发送解析命令的源地址。这样当使用DNS服务器递归查询后,DNS服务器响应给最初用户,而这个用户正是被攻击者。那么如果攻击者控制了足够多的肉鸡,反复的进行如上操作,那么被攻击者就会受到来自于DNS服务器的响应信息DDOS攻击。
如果攻击者拥有着足够多的肉鸡群,那么就可以使被攻击者的网络被拖垮至发生中断。利用DNS服务器攻击的重要挑战是,攻击者由于没有直接与被攻击主机进行通讯,隐匿了自己行踪,让受害者难以追查原始的攻击来。
2、DNS缓存感染
攻击者使用DNS请求,将数据放入一个具有漏洞的的DNS服务器的缓存当中。这些缓存信息会在客户进行DNS访问时返回给用户,从而把用户客户对正常域名的访问引导到入侵者所设置挂马、钓鱼等页面上,或者通过伪造的邮件和其他的server服务获取用户口令信息,导致客户遭遇进一步的侵害。
3、DNS信息劫持
TCP/IP体系通过序列号等多种方式避免仿冒数据的插入,但入侵者如果通过监听客户端和DNS服务器的对话,就可以猜测服务器响应给客户端的DNS查询ID。每个DNS报文包括一个相关联的16位ID号,DNS服务器根据这个ID号获取请求源位置。攻击者在DNS服务器之前将虚假的响应交给用户,从而欺骗客户端去访问恶意的网站。假设当提交给某个域名服务器的域名解析请求的DNS报文包数据被截获,然后按截获者的意图将一个虚假的IP地址作为应答信息返回给请求者。原始请求者就会把这个虚假的IP地址作为它所要请求的域名而进行访问,这样他就被欺骗到了别处而无法连接想要访问的那个域名。
4、DNS重定向
攻击者将DNS名称查询重定向到恶意DNS服务器上,被劫持域名的解析就完全在攻击者的控制之下。
探测目标ap,为伪ap的假设做准备,airodump-ng wlan0mon.
在上面探测ap中,我们可以了解到WiFi名称(ssid)、加密方式以及信道等信息。
接下来我们就可以启动伪ap了。airbase-ng wlan0mon -e "xiaoqin00" -c 6 (这里环境不同,启动方式也不一样,我这里是kali2.0的环境所以这样,以前版本启动方式为airbase-ng mon0 -e "xiaoqin00" -c 6)
这个时候我们就可收到我们伪造的ap了,但是这个ap无法行使正常的功能,你连接的话它会一直保持获取ip中的状态。
接下来就需要我们配置和这个伪ap配套的dhcp了。
apt-get install udhcpd
修改/etc/udhcpd.conf配置文件,自定义IP池、网关、DNS、interface等等。
推荐用gedit工具来编辑文件:
gedit /etc/udhcpd.conf
修改IP池
192.168.x.y
192.168.x.z
修改执行dhcp功能的接口
可以用过ifconfig -a或者iwconfig命令来查看接口
修改DNS、网关、netmask等
修改/etc/default/udhcpd,修改dhcpd功能为yes。
DHCPD_ENABLE="yes"
开启dhcp服务,service udhcpd start。
配置好dhcp后,我们还需要解决流量问题,这里192.168.2.0/24中的流量需要通过主机网卡eth0与外界进行交互。我们用iptables来解决这个。
再接着就是配置dns服务了。这里我们使用msf中的fakedns来提供dns服务。
我们将http://xiaoqin00.com的域名劫持到192.168.2.1上。
当这都配置好后,我们来连接这个伪ap。现在,只要被攻击者一进入www.xiaoqin00.com,他就会被劫持到错误的站点,这算是钓鱼的一种方法吧。
当你完成这些之前,如果目标主机已经连接上WiFi,可以对它的WiFi用mdk3等工具进行ddos攻击强制目标断开连接。
*在智能手机中,如果两个WiFi一样,那么手机会保持当前连接并对新出现的WiFi自动进行屏蔽。
*构造伪dhcp服务器有很多种方法。
5、ARP欺骗
ARP攻击就是通过伪造IP地址和MAC地址实现ARP欺骗,能够在网络中产生大量的ARP通信量使网络阻塞,攻击者只要持续不断的发出伪造的ARP响应包就能更改目标主机ARP缓存中的IP-MAC条目,造成网络中断或中间人攻击。ARP攻击主要是存在于局域网网络中,局域网中若有一台计算机感染ARP病毒,则感染该ARP病毒的系统将会试图通过”ARP欺骗”手段截获所在网络内其它计算机的通信信息,并因此造成网内其它计算机的通信故障。
ARP欺骗通常是在用户局域网中,造成用户访问域名的错误指向。如果IDC机房也被ARP病毒入侵后,则也可能出现攻击者采用ARP包压制正常主机、或者压制DNS服务器,以使访问导向错误指向的情况。
我们可以使用ettercap或者arpspoof来实现dns劫持。
6、本机劫持
本机的计算机系统被木马或流氓软件感染后,也可能会出现部分域名的访问异常。如访问挂马或者钓鱼站点、无法访问等情况。本机DNS劫持方式包括hosts文件篡改、本机DNS劫持、SPI链注入、BHO插件等方式。
本机dns劫持的操作比较简单,就是将目标主机的dns缓存给偷偷篡改掉。上面我们已经知道了dns的查询过程是先查询本地dns文件,找不到的话再去服务器查询。所以这种本机dns劫持的效果还是挺给力的。
就像这样,进入hosts文件改掉dns。等机主上机时,如果他要访问www.xiaoqin00.com的话,它的会话会被劫持到192.168.2.1上。
当然,你也可以破解路由器,直接更改路由器的dns设置。
五、如何防止DNS劫持(网络层面)
1、互联网公司准备两个以上的域名,一旦黑客进行DNS攻击,用户还可以访问另一个域名。
2、手动修改DNS:
-
在地址栏中输入:http://192.168.1.1 (如果页面不能显示可尝试输入:http://192.168.0.1)。
-
填写您路由器的用户名和密码,点击“确定”。
-
在“DHCP服务器—DHCP”服务中,填写主DNS服务器为更可靠的114.114.114.114地址,备用DNS服务器为8.8.8.8,点击保存即可。
3、修改路由器密码:
-
在地址栏中输入:http://192.168.1.1 (如果页面不能显示可尝试输入:http://192.168.0.1)
-
填写您路由器的用户名和密码,路由器初始用户名为admin,密码也是admin,如果您修改过,则填写修改后的用户名和密码,点击“确定”
-
填写正确后,会进入路由器密码修改页面,在系统工具——修改登录口令页面即可完成修改(原用户名和口令和2中填写的一致)
六、如何防止DNS劫持(应用层面)
本小节内容来自美图在HTTPS环境的DNS优化实践,通过该优化方案,使美图App请求耗时节约近半。
真实案例,提供给大家参考。
美图的移动端产品在实际用户环境下会面临 DNS 劫持、耗时波动等问题,这些 DNS 环节的不稳定因素,导致后续网络请求被劫持或是直接失败, 对产品的用户体验产生不好的影响。
为此,对移动端产品的 DNS 解析进行了优化探索,产生了相应的 SDK。在这过程中,参考借鉴了业内的主流方案,也进行了一些实践上的思考。
下面的内容会主要以 Android 平台来进行说明。
LocalDNS VS HTTP DNS
在长期的实践中,互联网公司发现 LocalDNS 会存在如下几个问题:
-
域名缓存: 运营商 DNS 缓存域名解析结果,将用户导向网内缓存服务器;
-
解析转发 & 出口 NAT: 运营商 DNS 转发查询请求或是出口 NAT 导致流量调度策略失效;
为了解决 LocalDNS 的这些问题,业内也催生了 HTTP DNS 的概念。
它的基本原理如下:
原本用户进行 DNS 解析是向运营商的 DNS 服务器发起 UDP 报文进行查询,而在 HTTP DNS 下,我们修改为用户带上待查询的域名和本机 IP 地址直接向 HTTP WEB 服务器发起 HTTP 请求,这个 HTTP WEB 将返回域名解析后的 IP 地址。
比如 DNSPod 的实现原理如下:
相比 LocalDNS, HTTP DNS 会具备如下优势:
-
根治域名解析异常: 绕过运营商的 DNS,向具备 DNS 解析功能的 HTTP WEB 服务器发起查询;
-
调度精准: HTTP DNS 能够直接获取到用户的 IP 地址,从而实现准确导流;
-
扩展性强: 本身基于 HTTP 协议,可以实现更强大的功能扩展;
那么,是否直接全部走 HTTP DNS 呢?
美图移动端 DNS 优化策略探索
HTTP DNS 相比 LocalDNS 存在一些优势, 然而 HTTP DNS 本身也是存在一定的成本问题。
美图的产品线丰富,涉及的域名也较为广泛,为了适应各产品的实际场景,在实践中设计了较为灵活的策略控制。
首先,在策略上并未完全放弃 LocalDNS。
一个 App 涉及的域名众多,在策略上能够配置其核心 API 域名走 HTTP DNS,而对于非核心请求仍希望它先尝试走 LocalDNS, 在异常情况下才升级走 HTTP DNS。
那么如何判断 LocalDNS 的异常情况呢?
选择一下三个指标来衡量一个 DNS 服务器的质量情况:
-
IP 记录的 TTL 时间: 在 DNS 劫持发生的情况下,返回的 TTL 可能会有非常大的值;
-
解析耗时: 如果一个 DNS 服务器解析耗时不理想,那么它也不是我们希望的;
-
返回的 IP 的可连接性: 对返回的 IP 进行质量测试,如果连接状况不佳,那么这个 DNS 服务器有劫持的可疑;
在 Android 平台上,通过系统方法获得的解析结果信息是非常有限的,上面的指标有的将无法获取,因此在实践中我们会自己去构造 DNS 查询报文,向运营商的多个 DNS 服务器发起查询。
通过上面几个指标的综合评定,当 LocalDNS 表现不佳的时候,策略上我们将升级走 HTTP DNS,尝试让用户获取更好的 DNS 解析效果。
在 DNS 解析环节,还有一个我们比较关心的指标,那就是 DNS 解析的耗时:
LocalDNS 在过期的情况下,会发起递归查询,这个时间是不可控的,在部分情况下甚至能达到数秒级别;
HTTP DNS 相对会好一些,但正常来看,也会有200ms 左右的耗时。
这个时间能否再优化一些呢?
我们 SDK 在本地构建了自己的记录缓存池,每次通过 LocalDNS 或是 HTTP DNS 解析得到记录都存在缓冲池中。
当然,这个是普遍的做法,系统底层的 netdb 库也是这样实现。
区别在于我们做了一个小改动:
对于过期的记录我们采用懒更新的策略,当查到过期的缓存记录时,先返回过期记录给用户,同时再异步重新发起 DNS 查询更新缓存记录。
这个小改动能够保证我们二次解析时都能命中本地缓存,极大地降低 DNS 解析耗时,不过它也带来了一定的风险性。
因此实践中,我们也会添加异步定期的 DNS 记录缓存池扫描功能,及时发现缓存中的过期记录并进行更新,也降低 App 命中过期记录的情况。
无侵入的 SDK 接入方式探索
在 DNS 优化的实践中,我们遇到最大的问题,倒不是策略层面设计问题,而是我们的 DNS SDK 运用到实际 App 产品业务上的姿势问题。
业内对 HTTP DNS 在实际业务中的接入方式多采用 IP 直连的形式,
即原本直接请求 http://www.meitu.com,现在我们先调用 SDK 进行域名解析,拿到 IP 地址比如 1.1.1.1,然后替换域名为: http://1.1.1.1/;
这样操作之后, 由于 URL 中 HOST 已经是 IP 地址,网络请求库将跳过域名解析环节,直接向 1.1.1.1 服务器发起 HTTP 请求。
在实际操作中,对于 IP 直连的方案我们踩了不少的坑。
首先,对于 HTTP 请求,采用 IP 直连的方案后,我们还是需要进行的一个操作是手动配置 Header 中的 HOST :
HTTP 协议相对比较容易,只需要处理 HOST,那么 HTTPS 呢?
发起HTTPS请求首先需要进行 SSL/TLS 握手,其流程如下:
-
客户端发送 Client Hello,携带随机数、支持的加密算法等信息;
-
服务端收到请求后,选择合适的加密算法,连同公钥证书、随机数等信息返回给客户端;
-
客户端检验服务端证书的合法性,计算产生随机数并用证书公钥加密发送给服务端;
-
服务端通过私钥获取随机数信息,基于之前的交互信息计算得到协商密钥并通知给客户端;
-
客户端验证服务端发送的数据和密钥,通过后双方握手完成,开始进行加密通信;
在我们采用 IP 直连的形式后,上述 HTTPS 的第三步会发生问题, 客户端检验服务端下发的证书这动作包含两个步骤:
-
客户端用本地保存的根证书解开证书链,确认服务端的证书是由可信任的机构颁发的。
-
客户端需要检查证书的 Domain 域和扩展域是否包含本次请求的 HOST。
证书的验证需要这两个步骤都检验通过才能够进行后续流程,否则 SSL/TLS 握手将在这里失败结束。
由于在 IP 直连下,我们给网络请求库的 URL 中 host 部分已经被替换成了 IP 地址,
因此证书验证的第二步中,默认配置下 “本次请求的 HOST” 会是一个 IP 地址,这将导致 domain 检查不匹配,最终 SSL/TLS 握手失败。
那么该如何解决这个问题?
解决 SSL/TLS 握手中域名校验问题的方法在于我们重新配置 HostnameVerifier, 让请求库用实际的域名去做域名校验,
代码示例如下:
我们又解决了一个问题,那么 IP 直连下, HTTPS 的问题都搞定了吗?
没有,HTTPS 还有 SNI 的场景要特殊处理。
SNI(Server Name Indication)是为了解决一个服务器使用多个域名和证书的SSL/TLS扩展。它的基本工作原理如下:
-
服务端配置有多个域名和对应的证书。客户端在与服务器建立SSL链接之时,先发送自己要访问站点的域名。
-
服务器根据这个域名返回一个合适的证书。
跟上面 Domain 校验的情况类似,这里的网络请求库默认发送给服务端的 "要访问站点的域名" 就是我们替换后的 IP 地址。
服务端在收到这样一个 IP 地址形式的域名后将是一脸懵逼,找不到对应的证书,最后只好下发一个默认的域名证书回来。
接下来发生的是,客户端在检验证书的 Domain 域时,怎么也检查不通过,因为服务端下发的证书本来就不是对应该域名的。
最后 SSL/TLS 握手失败告终。
上述这个 SNI 场景下的问题,我们是否有办法解决呢?
可以解决,需用客户端重新定制 SSLSocketFactory , 不过修改的代码相对较多,这里就不列举了。
如果我们 SDK 要接入到 App 实际业务中,到 HTTPS SNI 场景处理这里,相信很多同学都崩溃了,接入的工作量其实也不低。
很多情况下可能就做了妥协,只有 Okhttp 场景才使用这个 SDK,因为 Okhttp 本身支持 DNS 替换,没有上面那些问题。
在美图的实践中,我们不仅仅希望 Okhttp 的请求才进行这个 DNS 优化,我们希望在 App H5 页面加载、播放器播放等场景也能应用相应的优化。
在这样的需求下,IP 直连的接入方案带来的接入工作量其实不低,甚至需要改动到部分轮子。
在最初的实践中,我们也的确尝试了落实 IP 直连 到各个模块,然而即使克服了改造的工作量问题,实际运行上还是会有不少坑。
那么,有没有更合适的一种技术方案,能够降低 我们 DNS SDK 的接入工作量,也能兼顾各种使用场景,比如 HTTPS、RTMP 协议等?
基于这样的目标,我们在实践中尝试探索了一种对业务集成友好的无侵入式 DNS SDK 集成方案。下面我们以 Android 平台进行说明。
我们知道在 Java 层面上进行 DNS 解析的基本方式是调用如下方法:
Android 平台上常用的 Okhttp、HttpUrlConnection 等网络请求库都会依赖这个形式的 DNS 解析。
我们深入分析 InetAddress 的运行流程,其大致如下:
在上述流程中我们可以知道,InetAddress 会有到 AddressCache 尝试获取已缓存记录的动作,而这里 AddessCache 是一个 static 的 map 结构变量。
因此,在这里我们来对它做点小手脚 :
-
模仿系统的 AddressCache 构造一个我们自己的 AddressCahce 结构,不过它的 get 方法被替换为从我们 SDK 获取解析记录。
-
通过反射的形式用我们修改后的 AddressCache 替换掉系统的 AddressCache 变量。
这个偷天换日的操作之后,HttpsUrlConnection 等 Java 层网络请求在进行 DNS 解析时就会是这样一个流程:
通过这个形式,我们能够完美解决 Java 层的 DNS SDK 接入问题,对于业务方来说,他们并不需要做任何 URL 替换操作,对应的 HTTPS 场景下的问题也不复存在。
Java 层的接入解决了, 那么 Native 层呢?
我们知道在 Android 平台上,像 WebView、播放器等模块他们进行网络连接的操作都是在 native 层进行的,并不会调用到 Java 层的 InetAddress 方法。
首先在 C/C++ 层,我们知道进行 DNS 解析会使用 getaddrinfo 或是 gethostbyname2 这两个函数。
另外我们还知道,在 Android 等 Linux 系统下,对于 .so 这类可共享对象文件会是 ELF 的文件格式。
因此从这些已知信息,我们可以得到下列一些情况:
我们的 App 中 a.so 中直接使用到了系统 libc.so 中的 getaddrinfo 函数,
那么根据 ELF 文件规范,在 a.so 的 .rel.plt 表中会有如下关系定义: getaddrinfo ==> 0xFFFFFF 。
.rel.plt 表中的映射关系为 a.so 的运行指出了 getaddrinfo 这个外部符号在当前内存空间中的绝对地址。
正常情况下,a.so 中执行到 getaddrinfo 的函数流程是这样的:
那么在这里,我们是否可以手动修改这个映射表内容,把 getaddrinfo 的内存地址替换成我们的 my_getaddrinfo 地址呢?
这样,a.so 在实际运行时会被拐到我们的 my_getaddrinfo 中?
实际上,确实是可行的。 我们尝试在 SDK 启动后,对 a.so 的 .rel.plt 表进行修改,达到接管 a.so DNS 的目的,
修改后的 a.so 运行流程如下:
通过上面的方式,我们能够比较完美地接管 App 在 Java 层 和 Native 层 DNS 过程,实现业务方无任何额外改动的情况下运用我们的 DNS SDK 优化效果。
SDK 上线后的效果表现
在实际运用中,我们取得了比较好的效果。得益于 DNS SDK 在命中本地缓存率上的策略优化,我们的移动端产品在网络请求中 DNS 解析环节耗时得到降低,
从实际监控数据来看,完整网络请求的耗时也能够降低 100ms 左右。
通过 HTTP DNS 的引入和 LocalDNS 优化升级策略,我们的网络请求成功率有提升,在未知主机等具体错误率表现出下降的趋势。
由于 SDK 层面本身做好了灵活的策略配置,我们通过线上监控和配置也让各产品在效益和成本之间取得一个最佳的平衡点。
七、历史著名DNS劫持案例
-
2009年巴西最大银行遭遇DNS攻击,1%用户被钓鱼。
2009年巴西一家最大银行Bandesco巴西银行,曾遭受DNS缓存病毒攻击,成为震惊全球的““银行劫持案”。受到影响的用户会被重定向至一个假冒的银行网站,该假冒网站试图窃取用户密码并安装恶意软件。DNS缓存病毒攻击是利用互联网域名系统中的漏洞进行的,没有及时打补丁的ISP很容易受到攻击。合法的IP会被某个网站给取代,即使终端用户输入正确的网址也会被重定向至那些恶意网站。有近1%的银行客户受到了攻击,如果这些客户注意到了银行SSL证书在被重定向时出现的错误提示,就不会上当受骗。 -
2010年1月12日 上午7时40分 “百度域名被劫持”事件。
当时有网民发现百度首页登陆发生异常情况。上午8时后,在中国内地大部分地区和美国、欧洲等地都无法以任何方式正常登陆百度网站,而百度域名的WHOIS传输协议被无故更改,网站的域名被更换至雅虎属下的两个域名服务器,部分网民更发现网站页面被篡改成黑色背景以及伊朗国旗,同时显示“This site has been hacked by Iranian Cyber Army”(该网站已被伊朗网军入侵)字样以及一段阿拉伯文字,然后跳转至英文雅虎主页,这就是“百度域名被劫持”事件。 -
2012年10月25日 日本邮储银行、三井住友银行和三菱东京日联银行各自提供的网上银行服务都被钓鱼网站劫持。
据日本《日经电脑》报道,日本邮储银行、三井住友银行和三菱东京日联银行于2012年10月25日和10月26日分别发布公告提醒用户,三家银行各自提供的网上银行服务都被钓鱼网站劫持,出现要求用户输入信息的虚假页面,在登录官方网站后,会弹出要求用户输入密码等的窗口画面,本次虚假弹出式窗口页面的目的在于盗取用户网上银行服务的密码。这种弹出式窗口页面上还显示有银行的标志等,乍看上去像真的一样。 -
2013年5月6日 史上最大规模DNS钓鱼攻击预估已致800万用户感染。
-
2014年1月21日 北京2014年1月21日,全国大范围出现DNS故障,下午15时20分左右,中国顶级域名根服务器出现故障,大部分网站受影响,此次故障未对国家顶级域名.CN造成影响,所有运行服务正常。
本文链接:https://my.lmcjl.com/post/12333.html
4 评论