流量分类识别加密代理? 初探TLS in Any问题

流量分类? 这是什么闻所未闻的审查策略? 它离我们还有多远? TLS代理仍安全、隐蔽吗?

目录

📢首先回答上面提出的问题 请看VCR

  1. 流量分类,是指在不破坏数据原有加密、完整性的前提下,通过被动地记录流量的某些可见指标,来判断流量来源或目的地的某些特征,比如属于什么类型的客户端、什么类型的服务器、是什么用途的流量。

  2. 这种审查策略的确是闻所未闻的;在学术领域提出基于流量分类的审查策略之前,人们和审查者注意的重点都是将流量中特定位置的数据与某些数据集作匹配、或是观察客户端/服务端对精心构造的数据包的特定反应,以此来实现审查目的(而反审查工具的开发者、研究者们则配合网络测量等方式探索反审查策略)。

  3. TLS代理仍然安全,但是截至文章发布,所有已投入使用的开源TLS代理在下面提到的论文的实验数据中都属于隐蔽性存疑的一类,包括一些使用了多路复用的代理实现。

    注意: 在这里我们需要明确"安全性"和"隐蔽性"的区别。在反审查的语境下,安全性一般指在目前算力条件下,数据加密后在不可信通道中传输时的明文不可见性完整性;而隐蔽性一般指数据在不可信通道中传输时与某些特定数据负相似性,以及客户端和服务端与某些特定实现负相似性

下面我们进入正文。

之所以写这篇普及向的文章,是因为今年初我在Censored Planet上看到一篇名为"Fingerprinting Obfuscated Proxy Traffic with Encapsulated TLS Handshakes“的论文,在翻墙圈也引起了一些讨论,于是决定仔细研究下,希望能在此向读者解释一些核心概念和编者自己的想法。这个博客就是为了这篇文章而诞生的。

(注: 本文中所称"规避工具”,指用于规避网络流量审查的系统或协议)

ℹ️ 前言

目前被广泛认可的规避审查的策略,可以被抽象地归纳为两种:

  1. 隐写:使流量看起来像被允许的流量。
  2. 多态:使流量看起来不像任何其他流量。

“多态"就是shadowsocks等一众协议所采用的策略。他们将流量进行加密、填充、重排序等等操作,从而处理为随机字节流,使得审查者可见的流量中没有任何明文,审查者也就无法找出固定的正则式进行匹配,以此避免被识别并封锁。 (注:实际上互联网中完全随机的流量占比极低,因此随机本身便是一个特征,这也直接导致了中国大陆的GFW使用基于熵的规则(这里的意为数据的随机程度)粗略识别并封锁shadowsocks等规避工具,见论文 How the Great Firewall of China Detects and Blocks Fully Encrypted Traffic,当然后来反审查工具的开发者们采用诸如填充改变熵的设计规避了这一审查策略)

“隐写"的必要前提是至少要有一种被允许的流量,比如微信通话、TLS,接下来几段称这种被允许的流量为"该协议”。 “隐写"又可抽象地细分为两种:

  1. 模仿: 伪装自己是该协议下的某种实现进行传输。
  2. 隧道传输: 直接使用该协议下某种实现进行传输。

这俩隐写方式看起来似乎并没有太大区别,但实际上他们大相径庭。打个比方,“隐写"就像是给Windows电脑换Mac图标包、用仿Mac桌面,但不论如何底层都是Windows,始终运行不了访达 (MacOS的官方文件管理器)。而"隧道传输"就像是直接使用Mac,不会有任何"破绽”。

“模仿"被广泛认为不可能被用于规避审查,因为当宣称是该协议的某种实现时,必须实现该实现的种种不同反应、实现怪癖甚至Bug,否则审查者可以通过确认该实现是否具有本该有的这些细节,来辨别正常流量和规避流量。对于规避工具的开发者,常见的实现往往及其复杂,不可能实现完美的模仿。倘若规避工具模仿简单的实现,往往这个简单实现自身便不够常见,它们只会被审查者一起封杀。论文 The Parrot Is Dead: Observing Unobservable Network Communications更加周密且详细地分析了这一问题。

因此,“隧道传输"成为了被广泛使用的隐写方式。它通过调用某个常见实现提供的接口,将数据传递给这个接口并由该实现进行封装和传输。现在被广泛使用的基于TLS的规避工具都属于"隐写"中的这种类型。

🔍 TLS in Any问题是什么导致的?

流量之所以称作流量,正是因为它是由一个连接内的一系列数据包构成的,这些数据包有自己的大小、先后顺序。而每种类型的流量开头,往往会有为了一些满足特定需求而遵循特定规范的数据包,这些数据包被称作 “握手包”微信通话有自己的握手包,浏览网页用的HTTP有自己的握手包,而TLS为了实现数据的安全性与完整性,需要在连接开头以安全方式交换加密、认证用的数据(密钥和证书等),因此也会有自己的握手包。

正是TLS的这些握手包导致了TLS in Any问题。还记得前面对"握手包"的定义吗? 它们是 “一些满足特定规范” 的数据包。这个"规范"定义了握手包的结构、先后顺序、收发方向 —— 当规范规定了结构、而这一结构中的各项数据大小可预测时,这些握手包的大小其实已经变得可预测了。再加上TLS规范定义的先后顺序、收发方向与审查者对于握手包大小的估计值之间一一对应的关系,流量分类系统就可以粗略地区别出TLS流量。

举个栗子,这里规定一个人畜无害的Cat协议,它的握手包都在连接开始时被发送,它的规范中对结构、先后顺序、收发方向的规定如下:

  • 结构 & 先后顺序:
    • 第一个包: 200位的猫体重
    • 第二个包: 700位的品种编号
    • 第三个包: 1400位的猫猫喜爱程度
    • 第四个包: 1400位的猫粮剩余量
  • 收发方向
    • 第一个包: A -> B
    • 第二个包: A -> B
    • 第三个包: A <- B
    • 第四个包: A <- B

同时我们在传输前对Cat协议的每个包进行加密,使它们在加密后变大10-200位(现代密码学定义下的加密,只可能使数据在加密后的大小不变或增加)

那么流量分类系统在试图识别加密后的Cat协议时,只需要在每个连接开始时进行被动观察并这样判断:

  • 第一个包: 大小在210位到400位之间,方向是A -> B
  • 第二个包: 大小在710位到900位之间,方向是A -> B
  • 第三个包: 大小在1410位到1600位之间,方向是A <- B”
  • 第四个包: 大小在1410位到1600位之间,方向是A <- B

如果观察到某个连接的开头符合这种流量模式,那它很可能就是Cat协议。现代密码学定义下的加密至多只能使流量中各个握手包的大小在可预测的范围内浮动,并且加密不可能改变先后顺序和收发方向,因此这种流量模式是加密无法完全改变的,也就解决不了TLS in Any问题。

万一是误判怎么办? 误判的确是有可能的,然而现实中的用户每访问一个HTTPS网站,打开的TLS连接往往就有好几个,这些连接的开始都有TLS握手包被规避工具代理,因此这些连接都存在被流量分类系统识别的可能性。 只要误判的概率(假阳性率)远比正确识别的概率小,那么审查者只需要观察当某个用户在访问某台国外服务器时,是否有较多连接被流量分类系统识别为规避流量,再以此判断用户是否在使用规避工具。 由于误判概率远远小于正确识别概率,所以未使用规避工具的普通用户的被误判的连接一定较少,自然难以达到"被审查者判断为在使用规避工具"的程度。

🤔为什么TLS in Any问题需要得到重视?

  1. 先前已经有过中国大陆的审查者利用基于熵的规则粗略识别并封锁全加密代理的先例,然而这种审查策略在GFW.REPORT的实验(节7.2)中GFW的误判率(假阳性率)仍然高达0.6%。因为审查者面临着基本比率谬误问题,0.6%的假阳性率意味着当该审查策略被GFW广泛应用于所有流量时,也会导致误伤很多的正常流量。

    因此审查者需要使用额外的审查策略,从而在扩大审查范围时控制误伤数量。TLS in Any问题引出的基于流量分类的审查策略,正是一个绝佳的额外审查策略。这两个审查策略的共同运用将会有效提高审查者识别并封锁规避流量的能力。

  2. 本文开头提到的论文中,作者在论文第8节提出了两个针对现有规避工具的流量预分类器,分类标准相对粗略,但是它们也可以作为1.中所提到的**“额外的审查策略”**,在与其他审查策略共同使用的情况下进一步提高审查者的识别能力。

    • 预分类器1基于"TCP连接在外层TLS握手完成后的前15个包距离内的往返计数

      如何理解? 在正常的TCP连接中,服务端在每次收到来自客户端的数据包时,总会响应一个ACK包 (即TCP报头中ACK标志位的值设置为下一个服务端应当收到的包的SEQ序列号的值的TCP数据包) ,因此审查者通过记录在用户的TCP连接中经过审查者端的ACK包,即可知道在用户和服务器之间一共传输了几个来回的数据。我称之为 “往返计数”。在典型的HTTPS访问网站的过程中,用户发起的TCP连接在完成TLS握手后,通常只会有一个GET请求 (一次上行),还有来自Web服务器的一系列响应包 (一次下行),一共仅1次往返

      换作规避工具代理同样的HTTPS请求,在规避工具的TLS握手完成后,会先有1次(嵌套的)TLS握手被代理,然后才是上面提到的1次往返的HTTP请求过程。而这一个被代理的(嵌套的)TLS握手至少会给规避工具发起的连接增加1次往返。因此在使用该预处理器时,将往返计数较大的连接过滤出来,将能够避免对大多数正常流量的处理和误伤。

    • 预分类器2基于"TCP连接在外层TLS握手完成后的第一个包序列(burst)的总计大小

      如何理解? 在正常的TCP连接中,鉴于网络路径中的各个路由承载量不同,需要把一个较大的包分割为几部分(即分片),然后再按一定的时间间隔,逐个部分发送。逐个部分发送的过程中构成了一系列包,这原属于同一个包的一系列包称作一个包序列(burst)。由于规避工具在代理HTTPS请求时,需要代理1次(嵌套的)TLS握手,再加上加密带来的大小膨胀,会使得第一个包序列的总计大小比正常HTTPS流量的第一个包序列更大。

    这两个流量预分类器的分类标准尽管粗略,但是它们在开头提到的论文中的表现并不差。按照该论文第8节中的实验数据,当设定筛选标准为 预分类器1为小于2.5个往返计数、预分类器2为小于300个字节 时,这两个预分类器串连工作,能够筛选出82.5%的正常流量和1.5%的代理流量。

💡基于流量分类的审查策略带来的启示

谈完TLS in Any问题的流量分类识别原理后,我自己也作了两条总结,希望能帮助读者更加深入地理解问题本质。

  1. 包级别的混淆策略(对于单个包内的数据的填充、加密、重排序等)不足以对抗连接级别的审查策略,用流行词解释就是"降维打击”。

  2. 对于连接级别的特征(每个连接前n个包距离内的大小分布、往返计数,甚至包收发延时分布等)也应当通过填充包、分割包、延迟发包等方式进行处理。

🧰针对TLS in Any问题的设想规避策略

同时我也设想了以下几个存在可行性的解决方案 (规避策略),希望能够启发读者找到更加可行的规避策略。以下这些规避策略的本质都是尽量提高流量分类系统的误判率。

  1. 解构上行和下行连接 (缺点是可靠性较差)

  2. 分片发送被代理的TLS握手包,且在接收方接收到某个握手包的最后一个分片之前,对每一个已经接收到的分片都要作出假响应 (如构造校验和错误的包用于响应),避免审查者合并分片(重组流量)或产生更明显的特征。

    (这一策略已经在规避工具 Restls Protocol (Github仓库链接) 中得到了实践,然而该策略很可能会对用户体验产生显著影响)

  3. 切换到基于QUIC的规避工具,借助udp的不可靠性增加对其进行流量分类的复杂性,进而使审查者至少迟缓其审查技术的研发和部署进度。

    (很不幸,在2024年7月15日至18日中共举行第二十届三中全会期间,中国大陆出现了基于UDP的规避工具遭到大规模封锁的用户自发报告(链接来自LinuxDo论坛))

🧱审查者大规模部署基于流量分类的审查策略的几个挑战

  1. 附带损害仍然相当高。尤其是当下TLS1.3正在不断扩大部署规模,TLS1.2部署不断减少,而依照本文开头提到的论文中部署在美国运营商Merit Network的网络出口上的流量分类系统的实验数据,TLS1.3分类器以实验中约26%的真阳性率的模式运行时,其误判率(假阳性率)对比真阳性率略低(约为24%)的TLS1.2分类器,增加了40倍(0.025%→1%)。
  2. 对于使用激进的随机填充策略的规避工具(如XTLS-Vision, obfs4),审查者需要使用额外的定制分类器对流量进行处理;并且由于包大小不再适用于分类,误判率(假阳性率)理应有所增加。
  3. 对于既基于TLS、又使用激进的随机填充策略的规避工具,由于正常TLS流量中仅握手包具有较低的熵,理论上无法使用基于熵的规则对这些规避工具的代理流量进行预分类,这种情况理应会进一步提高误判率(假阳性率)。

(推荐相关阅读 : )

1.XTLS Vision, fixes TLS in TLS, to the star and beyond · XTLS/Xray-core · Discussion #1295 (github.com) (“生死五包"原始出处)