什么是深度包检测?
普通网络设备根据数据包要去哪里来转发它。深度包检测则查看它里面装了什么。正是这一点区别把路由器和审查系统分开,也正是它导致一个 VPN 这周还能用、下周就连不上。
浅层检测与深度检测
经过网络的每个数据包都带着一组嵌套的头部。最外层描述链路,然后 IP 头给出源地址和目的地址,再然后 TCP 或 UDP 头给出源端口和目的端口。普通路由器只需要这些就能完成工作:读目的地址、查表、转发。它从不打开负载,也不需要打开。
深度包检测就是越过这些头部、读进负载——也就是应用实际发送的那些字节。DPI 引擎串接在链路上,把构成字节流的数据包重新组装起来,然后试图识别正在使用什么协议,并在力所能及的范围内识别在说什么。之所以叫「深度」,是因为它比路由所需要的更深地进入了数据包。
- 浅层过滤按地址和端口封锁:「丢弃所有发往 203.0.113.10 的流量」,或者「丢弃所有发往 1194 端口的流量」。它便宜、粗糙,改个地址或端口就能轻易绕过。
- 深度检测按行为封锁:「丢弃任何看起来像 OpenVPN 握手的流量,无论它发往哪里、走哪个端口」。运行成本高得多,而改地址或端口无济于事。
DPI 引擎究竟在看什么
DPI 引擎很少试图完整理解一个协议。它寻找能可靠区分协议的最廉价信号,因为它必须在一条承载着数百万条流的链路上于几毫秒内作出判断。实践中它依赖四类信号。
- 字面字节特征
- 出现在已知偏移处的固定字符串或结构。TLS 记录的头几个字节是可辨识的;SSH 横幅、BitTorrent 握手,或 OpenVPN 控制包的操作码布局也是。在已知偏移处匹配一个特征几乎不花什么代价。
- 加密协议内部的明文元数据
- 加密并不隐藏一切。标准 TLS 握手会在 Server Name Indication 字段里明文发送所请求的主机名;在较旧的 TLS 版本里,服务器证书也是可见的。对一条本来不透明的连接来说,这些元数据是过滤器所能拿到的最丰富的信息来源。
- 握手指纹
- 即使内容是加密的,协商的形态却不是。客户端提供的密码套件的确切列表、TLS 扩展的顺序、前几条记录的大小——浏览器、手机应用和 VPN 客户端在这些方面各不相同,并且可以被哈希成一个指纹。
- 统计与时序行为
- 数据包大小、上传与下载之比、连接随时间的节奏。视频通话、文件下载和同时承载一切的隧道各有可区分的特征,分类器无须读取负载中的任何一个字节就能据此训练。
实践中最重要的是前两类。它们精确、廉价,且极少误报,这就是过滤系统首先采用它们的原因,也是协议设计者把大部分精力花在消除它们上的原因。
加密隐藏了什么,没有隐藏什么
人们常常以为加密连接对观察者来说是不透明的。并非如此。加密保护的是负载的机密性。它本身并不隐藏连接的存在、两端是谁、有多大、持续多久,或者正在使用什么协议。
| 属性 | 可见吗? | 说明 |
|---|---|---|
| 你的 IP 地址 | 可见 | 它在 IP 头里;必须在,否则回包到不了你这里。 |
| 目的 IP 地址 | 可见 | 同样的道理。反向 DNS 往往会暴露运营方。 |
| 目的端口 | 可见 | 暗示了服务:HTTPS 是 443,SSH 是 22,诸如此类。 |
| 所请求的主机名 | 通常可见 | 除非启用了 Encrypted Client Hello,否则会在 TLS SNI 字段中明文发送,而 ECH 目前远未普及。 |
| 页面内容 | 不可见 | 握手完成后由记录层保护。 |
| 传输字节量与时序 | 可见 | 加密从不隐藏这些。填充可以模糊它;没有什么能消除它。 |
| 客户端软件指纹 | 通常可见 | 握手本身是在任何密钥协商之前发送的。 |
实际后果是:过滤器不需要解密任何东西就能作出封锁决定。它只需要根据那些从未被加密的部分,判断这条连接是否属于它被要求丢弃的类别。这是个容易得多的问题,也正是现代审查系统所要解决的问题。
判决如何执行
一条流被分类之后,系统必须据此行动。所选的方式很能说明这套部署的性质,而且通常可以从客户端一侧分辨出来。
- 静默丢弃。数据包被丢掉,不作任何回应。连接看起来卡住,最终超时。这是不想暴露自己的过滤器最常见的行为。
- 伪造重置。系统注入一个 TCP RST 包,伪装成来自对端。连接骤然死亡,往往在若干数据包已经通过之后——这是个很强的线索:真正不可达的服务器根本不会应答。
- DNS 操纵。域名查询被返回错误地址,或者干脆没有地址,连接甚至还没尝试建立。这很便宜,通过比较不同解析器的答复就能轻易发现。
- 限速。流被放行,但被限制到无法使用的程度。归因更难,因为不经仔细测量就与普通拥塞无从区分。
- 主动探测。系统不当场封锁,而是记下这个端点,稍后自己去连接它、模仿客户端,以确认那里运行着什么。如果服务器的回应方式只有代理才会有,这个地址就会被加入封锁列表。
这为何塑造了隧道协议的设计
一旦接受过滤器读的是握手和元数据而非负载,隧道的设计目标就彻底变了。仅仅做到加密已经不够。连接必须避免呈现任何能把自己与过滤器决定放行的流量区分开来的信号——在今天的互联网上,那意味着通往普通 Web 服务器的普通 TLS。
这就是为什么本资料库中讨论的那些协议都收敛到同样的几个思路上:把协议特有的字节特征从线路上抹掉,让 TLS 握手指纹与真实浏览器一致,避免发送本身就是信号的主机名,以及在面对未经认证的探测者时,回应得和普通 Web 服务器一模一样。这些做法,每一条都是为了消除上面某一个信号而存在的。
同样,这也是为什么对隧道协议的任何诚实描述都不会承诺彻底击败 DPI。过滤运营方随时可以扩大策略——例如把流量类别从例外封锁改为默认封锁——而面对一个干脆拒绝一切无法确认之物的网络,没有任何协议设计能够幸存。
常见问题
深度包检测能读取我的加密流量吗?
只要加密本身可靠,且网络没有出示一份被迫让你的设备信任的证书,它就读不到负载。它能读到的是加密负载之外的一切:两端的 IP 地址、端口、流量的大小和时序、握手指纹,以及在大多数连接中你所请求的主机名。
深度包检测合法吗?
这完全取决于司法辖区以及由谁来做。网络运营商出于安全或容量原因检查自己的流量是常规做法,通常也是合法的;由其他方进行的拦截通常不是。这是法律问题而非技术问题,值得查一查你所在地适用的规则。
VPN 能阻止深度包检测吗?
VPN 改变的是检测能看到什么,而不是阻止检测本身。你的流量内容对本地网络变得不透明,但通往 VPN 的连接本身依然可见,并且可以被分类。能否被正确分类,取决于隧道与普通流量的相似程度。
我怎么判断封锁我的是不是 DPI?
线索在于失败的形态。连接建立后骤然中断,说明可能是注入的重置;毫无回应地卡住,说明是静默丢弃;同一个域名在不同解析器上解析结果不同,说明是 DNS 操纵。如何按顺序排查,见排障指南。