具有反审查性的网络技术
TLS
TLS(Transport Layer Security,传输层安全管理协议)是一种旨在保护互联网通信安全的加密和身份验证协议。其前身是 SSL(Secure Sockets Layer,安全套接层协议),SSL 协议是为 HTTP 开发的安全协议,在互联网访问的基本原理 - 连接成功建立,使用 HTTP 协议传输内容中,具体讲述了其实现 HTTPS 的方法。尽管 SSL 被 TLS 所取代,但两者实现的逻辑是一样的,且「SSL」这个名称如今仍在被广泛使用。
TLS 握手是启动 TLS 通信会话的过程。在 TLS 握手过程中,通信双方交换消息以相互确认,彼此验证,确立它们将使用的加密算法,并生成一致的会话密钥:
-
首先,客户端向服务器发送一个 Client Hello 消息,该消息将包含客户端支持的 TLS 版本,支持的密码套件,以及称为一串被称为「客户端随机数(client random)」的随机字节。
-
服务器收到 Client Hello 之后,以 Server Hello 消息响应,内含服务器的 SSL 证书、一个公钥、服务器选择采用的密码套件,以及「服务器随机数(server random)」,即由服务器生成的另一串随机字节。
-
客户端使用颁发该证书的 CA(证书发布机构)验证服务器的 SSL 证书。同时,再发送一串随机字节,即「预主密钥(premaster secret)」。预主密钥使用上一步服务器发送的公钥加密。
-
由于服务器肯定拥有自己发送的公钥对应的私钥,因此得以对预主密钥进行解密,现在服务器安全地得到了一个相同的预主密钥。
-
客户端和服务器均使用预主密钥、客户端随机数、服务器随机数,通过一系列 TLS 协议定义好的伪随机函数,生成对称加密的会话密钥,这一密钥也应是相同的。
-
客户端发送一条「已完成」消息,从这条消息开始,双方的所有消息都基于会话密钥加密。
-
服务器回复一条「已完成」消息。至此,TLS 握手完成,双方可使用会话密钥继续加密通信。
TLS 1.3
TLS 1.3 弃用了对于 RSA(一种常用的非对称加密算法)的支持,也弃用了一批支持易受攻击的密码套件和参数。最重要的是,TLS 1.3 对 TLS 握手做了调整,压缩了双方的协商过程,把握手时间减少到了一个消息往返(1-RTT),交换 SSL 证书的部分也使用了加密传输,使得第三者难以从 TLS 握手中得知客户端的访问域名,让握手更快更安全。
QUIC 协议
QUIC(读作「quick」)是一个通用的传输层网络协议,最初最初是「快速 UDP 互联网连接(Quick UDP Internet Connection)」的首字母缩写,由 Google 的 Jim Roskind 设计。该协议于 2012 年实现并部署,2013 年随着实验范围的扩大而公开发布。
QUIC 通过 UDP 协议在两个端点之间建立若干个多路连接,提高了目前使用 TCP 的面向连接的网络应用的性能,以达到在网络层淘汰 TCP 的目标。因为其设计目标在于取代 TCP 协议,该协议也被称为「TCP/2」。
QUIC 与 HTTP/2 的多路复用连接协同工作,允许多个数据流独立到达所有端点,因此不受涉及其他数据流的丢包影响。与之相比,HTTP/2 建立在 TCP 协议上,如果任何一个 TCP 数据包延迟或丢失,所有多路数据流都会遭受队头阻塞延迟(队列中的第一个数据包因阻塞无法处理,导致其后的数据包即使本身没有阻塞,也无法被及时处理和传递的现象)。
HTTP/2
HTTP/2 是 HTTP 的第二个主要版本,主要目标是提高 Web 性能和减少网络延迟。与基于文本的 HTTP/1.1 不同,HTTP/2 采用二进制分帧传输数据,并引入了多路复用、头部压缩和服务器推送等特性,使得在单个 TCP 连接上可以同时处理多个请求和响应,从而显著提升页面加载速度和效率。
QUIC 的次要目标包括降低连接和传输时延,以及每个方向的带宽估计以避免拥塞。它还将拥塞控制算法移到了两个端点的用户空间,而不是内核空间。
用户空间与内核空间
用户空间是指普通应用程序运行的区域,受限制较多,不能直接访问系统资源,而内核空间是指操作系统核心代码运行的区域,拥有高权限且受保护。操作系统通过在用户空间和内核空间之间设置隔离,利用系统调用接口,确保了系统的稳定性和安全性。
在连接建立时,QUIC 将 TLS 1.3 握手整合到连接建立过程中,并视情况把握手过程分为两种:1-RTT 和 0-RTT。在重连时,QUIC 可以通过缓存的方式降低握手延迟。此外,QUIC 在最后一次握手时,会生成一个会话密钥,这样即使服务器的长期 DH 值(为快速握手而设计)被破获,且生成了用于握手的初始密钥,也无法对会话中的数据进行解密。
QUIC 已成为 HTTP/3 的底层传输协议,能显著提升网页浏览的性能与安全性。
加密 DNS
DNS 作为互联网的电话簿,在互联网中具有举足轻重的作用。然而,互联网在设计之初并没有充分考虑到安全性,几乎所有实现互联网的基础协议与工具,包括 TCP/IP 协议、DNS、HTTP 协议等都是在未加密的状态下工作的。由于传统 DNS 查询都以 UDP 或 TCP 协议明文传输,极易被第三者监听和篡改,为了解决这个问题,加密 DNS 应运而生。
需要注意的是,加密 DNS 仍属于比较新的网络技术,当下的网络设备、操作系统以及浏览器对于加密 DNS 的支持仍有局限。
DNS over TLS (DoT)
DoT 是通过 TLS 协议来加密并打包 DNS 的安全协议。传统 DNS 查询的运作方式非常简单:客户端直接向 DNS 服务器发送一个 DNS 请求,然后服务器就会回复出一个 DNS 响应。然而,在使用 DoT 的情况下,和实现 HTTPS 一样,在进行 DNS 查询之前,双方需要完成 TLS 握手后,才能进行 DNS 查询和响应。这使得第三者既无法了解客户端正在查询的域名,也无法对其进行修改。
不同于传统 DNS 服务默认运行在 53 端口,DoT 服务默认运行在 853 端口。
DNS over HTTPS (DoH)
DoH 的实现原理和 DoT 大致相同,只不过 DoH 是通过安全的 HTTPS 流传输 DNS 查询和响应消息,而 HTTPS 流本身就是建立在 TLS 握手之后的。
由于使用 HTTPS 流,和 HTTPS 服务共用端口 443,因此不用担心服务端口被网络设备防火墙单独拦截的问题。
DoH 和 DoT 一样,默认使用 853 端口。
DNS over QUIC (DoQ)
DoQ 使用 QUIC 协议进行加密 DNS 解析,由于 QUIC 协议本身使得队头堵塞问题得以解决,能使其在更为苛刻的网络环境中表现得更好。此外,由于 QUIC 协议的速度优势,使其解析速度能达到 DoT 的两倍。
DNSCrypt
DNSCrypt 是由 Frank Denis 及付业成主导设计的加密 DNS 通信协议,可以使用 UDP 端口或 TCP 端口运行,尽管与 HTTPS 完全不同,但两种情况下的默认端口号均为 443。
DNSCrypt 客户端必须明确信任所选提供者的公钥,想使用哪个 DNSCrypt 服务器,就需要预先安装该服务器的公钥,而不是通过常规浏览器中受信任证书颁发机构列表获取信任。
DNSCrypt 是较为早期的 DNS 的加密手段,出现时间比 DoH 和 DoT 早。尽管其能有效阻止在传输过程中对 DNS 请求和响应的篡改,但其从未被标准化为互联网请求意见稿(RFC),且其主要依靠特定客户端和 DNS 服务器的实现,因此,DNSCrypt 的应用不如上述方案广泛。
请求意见稿
请求意见稿(Request for Comments,RFC)是由互联网工程任务组(IETF)发布的一系列备忘录。文件收集了有关互联网相关信息,以及 UNIX 和互联网社群的软件文件,以编号排定。
RFC 始于 1969 年,由当时就读加州大学洛杉矶分校的 Stephen D. Crocker 用来记录有关 ARPANET 开发的非正式文档,最终演变为用来记录互联网规范、协议、过程等的标准文件。基本的互联网通信协议都有在 RFC 文件内详细说明。RFC 文件还额外加入许多的论题在标准内,例如对于互联网新开发的协议及发展中所有的记录。
ESNI
ESNI(Encrypted Server Name Indication)意为加密的服务器名称指示,要理解 ESNI,首先就先要了解什么是 SNI。
SNI(Server Name Indication,服务器名称指示)是 TLS 的一个扩展协议,其允许在服务器同一个 IP 地址上承载多个 HTTPS 网站的 SSL 证书。具体工作原理是在 TLS 握手过程中,客户端会告知服务器它正在连接的域名,这样服务器就能在握手时选择正确的证书进行安全连接,从而解决了早期 HTTPS 服务无法支持多域名的问题。
由于 IPv4 地址池的紧张与 IPv6 建设的滞后,在同一个 IP 地址上承载多个 HTTPS 网站的需求逐渐变得必要。然而,在没有 SNI 之前,一个 IP 地址只能绑定一个 SSL 证书,如果一个服务器上托管了多个域名,客户端在进行 TLS 握手时,无法知道它应该使用哪个证书建立安全连接,进而导致 TLS 握手报错。可以简单地将 SNI 想象为房间号码:一栋大楼中有多个房间,因此每间房间都需要一个不同的编号来加以区分。类似地,虽然服务器由 IP 地址标示,但是客户端设备需要在其发送给服务器的第一条消息中包括 SNI,以指示其试图访问哪个域名(哪间房间)。
然而,SNI 虽然作为 TLS 握手的重要组成部分,但只有在使用 SNI 成功完成 TLS 握手后,才能进行加密。这意味着 SNI 信息本身不会被加密,任何监视客户端和服务器之间连接的攻击者都可以读取握手的 SNI 部分,以此确定客户端正在与哪个网站建立连接。GFW 可以轻易通过嗅探 SNI 来判断用户是否在访问受到封禁的网站。
为了解决这个问题,ESNI 应运而生,ESNI 通过强制使用 TLS 1.3 加密客户端问候消息的 SNI 部分来保护 SNI 的私密性。
ESNI 通过公钥加密传递 SNI。简而言之,服务器会在其 DNS 记录中添加一个公钥(服务器自己保留用于解密的私钥),这样,当客户端执行 DNS 查询时,同时能找到该服务器的公钥。然后,客户端即可使用公钥来加密 SNI 记录并发送给服务器(由于公钥加密是非对称加密,即便第三者拿到了公钥,也无法仅靠公钥逆推出私钥)。
对于审查者而言,ESNI 让审查 HTTPS 流量变得更加困难,因为不知道用户使用 ESNI 访问的网站,审查者要么不封锁任何 ESNI 连接,要么封锁所有的 ESNI 连接。目前,GFW 已被证实通过丢弃从客户端到服务器的数据包来阻止所有 ESNI 连接。
因此,几乎可以说,如今的 GFW 无法审查使用加密 DNS+ESNI+TLS 1.3 技术访问的 HTTPS 流量,只能采取粗暴的「一刀切」方式——封锁一切使用 TLS 1.3 和 ESNI 的加密 HTTPS 流量。
ECH
ESNI 的 RFC 从版本 6 开始就从加密 SNI 转移到了加密整个 clientHello(TLS 握手的第一步),标题也从版本 7 开始就从 Encrypted Server Name Indication(ESNI)变成了 Encrypted Client Hello(ECH,加密客户端问候)。
域前置
域前置(Domain fronting)是一种隐藏连接真实端点来规避互联网审查的技术。其主要原理为访问网站时,在明文的 DNS 请求和 SNI 中使用无害的域名来初始化连接、公布给审查者,而实际要连接的敏感域名仅在建立加密的 HTTPS 连接后发出。
域前置成功实施与否,取决于服务器对 SNI 的处理逻辑:在 SNI 中,客户端与服务器建立连接的时候就发送要连接的域名,以便服务器可以返回一张颁发给指定域名的证书。那么,当用户在 SNI 中请求了一个不存在于服务器的域名,服务器应该如何应答呢?
如果服务器为了连接的进一步进行,选择发送一张默认的服务器证书,那么,客户端便可以进一步使用这个证书与服务器进行连接。然而,如果服务器在收到这样的数据包后没有返回这个泛域名证书而是拒绝连接,那域前置自然无法实施。