在互联网上,每台计算机和网络设备都使用唯一的IP地址标识自己,这个地址由一系列数字组成,例如IPv4地址可能是类似于192.168.1.1
的形式。然而,记住每个网站和网络服务的IP地址是非常困难的,网络设备的IP地址也可能发生变化,因此DNS被创造了出来。
DNS(Domain Name System)是网络中用于将域名解析为IP地址的系统。DNS的作用可以理解为一个分布式数据库,用于将域名映射到对应的IP地址,我们的电脑使用DNS协议和DNS服务器交互,DNS服务运行于UDP和TCP的53端口(通常使用UDP,极少使用TCP)。
为了理解DNS的作用,我们必须先了解域名相关的概念。
全世界的最高域名管理机构是ICANN(Internet Corporation for Assigned Names and Numbers),总部位于美国。当然,ICANN不会亲自管理世界上每一个域名,而是将域名交给各级托管商来具体管理。例如.cn
顶级域名就是由CNNIC(中国互连网络信息中心)管理。
域名:一串用点号.
分隔的字符串组成的名字,用来标识网络上的一个计算机或服务器,例如www.google.com
。
根域名:实际上例如www.google.com
的域名也可以写作www.google.com.
,最后的这个点号就是根域名。根域名是个抽象的概念,理论上所有域名都需要从根域名查起,由根域名服务器开始一级一级的向下查询。根域名列表由ICANN维护,里面记载着顶级域名和对应的托管商。
顶级域名:顶级域名是最高层级的域名,例如常见的.com
、.org
等通用顶级域名,以及各个国家管理的.cn
、.us
等国家顶级域名。
二级域名:我们自己注册域名时,通常会选择一个顶级域名然后在其之上自己起名字,二级域名就是指域名注册人注册时起的这个名字,如google.com
、gacfox.com
等。以此类推,三级域名就是二级域名的子域名,例如www.google.com
、blog.gacfox.com
。
我们发起DNS查询时,响应DNS查询的服务器就是DNS服务器。整个DNS系统中,DNS服务器有不同的层级(Zone)。
根域名服务器:根域名服务器中维护着根域名列表,其中记录了顶级域名和对应的托管商,由ICANN维护。根域名服务器所在的层级也叫根区(Root Zone)。全世界有13个根域名服务器,10台位于美国,其余位于荷兰、瑞典和日本。
顶级域名服务器:顶级域名服务器负责管理该顶级域名下的所有二级域名,服务器中记录了二级域名和IP的映射关系。
权限(权威)域名服务器:按照上面的逻辑,顶级域名服务器之下的DNS服务器应该是专门管理注册在二级域名下的子域名的,但实际不是这样。这是因为二级域名非常多,我们不可能为每个二级域名设置一个DNS服务器。权限域名服务器是按分区自由配置的,例如我们有gacfox.com
二级域名,我们设置权限域名服务器管理gacfox.com
,它负责解析www.gacfox.com
、oss.gacfox.com
,我们还设置了blog.gacfox.com
的权限域名服务器,用于解析blog.gacfox.com
,这些权限域名服务器可能处理相同或不同层级的域名,但它们是同等地位的,我们可以相对自由的进行配置的划分。
本地域名服务器:本地域名服务器是配置在我们的计算机中的,它可能是手动配置的,也可能是DHCP下发的。我们的计算机发出DNS查询请求时,这个请求会先发给本地域名服务器,本地域名服务器负责管理本地域名的解析,也负责向上级域名服务器查询。
DNS服务器会在请求后返回资源记录,DNS资源记录有不同的类型,下面是一些常用的记录类型。
A:记录指定域名对应的IP(IPV4)。
AAAA:记录指定域名对应的IP(IPV6)。
CNAME:别名记录,将一个域名映射到另一个域名。
TXT:文本记录,用于直接返回一些自定义的文本信息。
NS:域名服务器记录,返回该域名由哪些域名服务器解析。
一般来说,我们正常访问网站时就会请求A
或AAAA
记录,偶尔也有使用CNAME
的小网站,这也是我们在CloudFlare等服务提供商的控制台中要配置的内容,其余类型记录一般用于返回一些扩展信息,用于其他用途。
域名解析实际上可以设计成两种查询方式,这里假设有根域名服务器为Root,顶级域名服务器为A,权限域名服务器为B:
递归查询:客户端查询Root,Root因此查询A,A因此查询B,获得结果后逐级返回,最终结果由Root返回给客户端。
迭代查询:客户端查询Root,Root告诉客户端去A查询,查询A,A告诉客户端去B查询,最终结果由B返回给客户端。
尽管DNS协议在设计上支持这两种查询方式,但通过上面介绍我们可以发现,完全使用递归查询时,根域名服务器的压力将非常大,因此DNS实际上仅会在计算机和本地域名服务器这一级是递归查询的,后续都是迭代查询的。
假如我们使用域名访问一个网站,DNS查询具体流程如下:
当然,实际上DNS查询不会每次请求都去逐级迭代查询,浏览器、操作系统和本地域名服务器都有缓存,使用缓存能够加快域名解析的速度,也能进一步减轻上级域名服务器的压力。
DNS协议使用UDP和TCP的53端口,默认使用UDP作为传输层。出于网络设备的MTU考虑,基于UDP的DNS报文被限制为512字节,当超过该大小时报文将被标识为被截断,此时客户端得知收到的DNS回复的不完整的,将再次尝试使用TCP发送DNS查询,不过这种情况极少出现,某些DNS服务器可能并不支持TCP。
DNS协议的报文分为请求报文和响应报文,格式均如下图。
事务ID:报文的ID,用于对应请求和应答。
标志:报文中的标志位,具体格式为Response Opcode Authoritative Truncated RecursionDesired RecursiveAvailable Z ReplyCode
:
问题计数和回答资源记录数:DNS查询和请求的数目。
查询问题区域:指DNS查询的具体请求内容,包括查询域名(反向查询则为IP地址),查询类型(如A表示IP V4域名解析查询,AAAA则是IPV6的域名解析查询),查询类(通常设置为1,表示互联网地址)。
回答问题区域:回答问题区域的内容也被称为资源记录,内容包括查询域名、查询类型、查询类、生存时间(用于指导缓存)、资源数据长度、资源数据(具体的查询结果)。
权威名称服务器区域:内容格式同回答问题区域内返回的资源记录,但返回的内容是相关权威域名服务器的。
下面是一组DNS请求和应答的例子,例子中我们查询了oss.gacfox.com
域名。
DNS协议诞生于1997年,随着时代的发展,DNS协议虽然仍广泛使用,但它已经面临严峻的安全问题。首先,DNS报文的基于UDP明文传输的,任何人都可以轻易监听到网络内的DNS查询,观察用户在访问哪些网站;其次DNS响应也存在被篡改的风险,攻击者可以篡改DNS响应甚至DNS服务器的缓存,借此可以将用户引导到钓鱼网站,这种攻击方式被称为DNS欺骗或DNS污染。
为了解决这些弊端,后来诞生了好几个网络协议以解决这些问题。
DNSSEC(淘汰):DNSSEC在DNS的基础上增加了签名机制,达到防篡改的目的,数字签名采用了非对称加密方式生成,并通过信任链机制保证整条DNS查询链的安全。不过DNSSEC仍未考虑保密性,现在流行的浏览器也从未支持过该协议。
DNSCrypt(极少使用):DNSCrypt是一个重新设计的加密DNS协议,它能够实现防篡改和加密,但该协议从未通过IETF进行标准化,支持该协议的服务器不多,也没有浏览器支持。
DoT(较少使用):DoT基于TLS实现了DNS报文的安全传输,这类似HTTP和HTTPS的关系,加密和信任链均由TLS保证。不过目前DoT的实际应用较少。
DoH(主流):DoH基于HTTPS实现了DNS报文的安全传输,DoH出现最晚,但由于HTTP协议十分成熟而且具有极高的泛用性,大部分编程语言都能轻易开发出DoH的客户端和服务器,DoH因此迅速被广泛支持,CloudFlare等主流的DNS服务提供商和Firefox、Chrome等主流浏览器也均支持DoH。不过DoH最明显的缺点就是性能较差,实际使用时和传统DNS查询有较为明显的速度差距。
在Firefox浏览器中,我们可以在Enable DNS over HTTPS using
设置中配置DoH,防止DNS被监听和污染。Firefox浏览器内置了CloudFlare和NextDNS的选项,在中国大陆地区则可以选择国内的DNSPod等服务商提供的DoH服务。