吕英豪, 陈嘉耕
华中师范大学计算机学院信息安全系 武汉 中国 430079
自互联网诞生以来, 各种攻击和窃密技术层出不穷。在各类网络边界中, 小至家庭、企业, 大至ISP(Internet Service Provider, 互联网服务提供商), 都存在不同形式和不同程度的劫持和窃密威胁[1]。为保护网络通信的机密性和完整性, 某种安全代理有时是必要的。为使讨论更加便利, 下文提到“网络攻击”均指网络劫持和窃密等安全代理保护范围的攻击, 不包括其他类型的攻击。同样的, 下文提到的“防御”即对上述攻击的防御。
安全代理的设计策略主要有六种[1], 分别为附带损害(Collateral Damage)、进入影响范围外(Outside Scope of Influence)、速率限制(Rate Limit)、通信解耦(Decoupled Communication)、淹没(Overwhelm)和无目标(No Target)。附带损害意味着攻击者必须面对由于封锁某一特定协议、服务或应用带来的巨大附带经济损失; 进入影响范围外通常指寻找跨越边界的实体或不受攻击的实体; 速率限制可能是对通信的限制(信道使用频率、吞吐量等)或对攻击效率的限制 (验证码等); 通信解耦主要指非对称通信(可能是路由路径、时间、协议、应用等的非对称)和带外传输; 淹没通常是指大量部署服务端或通信节点; 无目标指使非法流量难以分辨(随机化或隧道化等)或隐藏网络地址(路由或人工传递等)。其中后四种的作用在于减少被发现的机会并使攻击者消耗更多资源, 直到攻击变得非常不经济, 从而迫使攻击者放弃攻击。在某一具体防御方案中通常会综合运用两到三种策略。
发展至今, 具有代表性的工具大约有二十余种, 但只有一种策略组合(附带损害+无目标)是广泛使用的、有效的和高性能的。代表协议有Obfs4、Shadowsocks、TLS隧道、Lampshade和Obfuscated SSH, 对应的工具为Tor、Shadowsocks、Trojan、Lantern和Psiphon。让我们重点考察当前最为流行的TLS隧道代理技术。Trojan完全把代理协议封装在TLS信道中, 并通过检测TLS握手阶段是否按照约定进行特殊处理来认证身份, 认证失败则提供正常的HTTPS服务。V2Ray的生态完善, 同时支持多种协议, 但抛开锦上添花的额外特性不谈, 实际可用的协议只有TLS隧道一种, 经过配置也可实现类似Trojan的功能但不如Trojan隐蔽。基于上述讨论, 我们认为基于“附带损害+无目标”的思路最有利于打造出长期稳定有效的协议。
文章结构安排如下: 第2章阐述攻防现状并为本协议的设计策略作背景说明; 第3章详细描述本协议的设计方案和处理细节; 第4章对协议进行安全性分析; 第5章从安全性和性能两方面与其他工具进行比较分析; 第6章对应用场景和使用方法等进行进一步的探讨; 第7章总结全文。
在互联网松散的结构下, 没有被保护的数据面临着非常大的被窃取机密信息或通信被劫持的风险。为保护网络通信而发展起来的安全代理技术与相应攻击技术之间的对抗由来已久, 现简述现有攻击技术和防御技术的原理和局限性。
对攻击者的假设: 攻击者受限于计算资源和经济上的利益, 不会彻底阻断网络也不会采用白名单策略。一个更强的假设是, 攻击者的攻击设备是旁路的而非串接的[2]。
对防御者的假设: 可以通过某种合法途径不借助于已经建立的安全代理通信信道访问外部信息。
基本攻击手段: 攻击过程包含两个阶段, 发现和阻断。发现的基本手段是DPI和单包探针。阻断的基本手段是DNS投毒和IP黑名单。DPI技术检查应用层数据; 单包探针查看服务响应; DNS投毒对特定域名的DNS请求会抢先回复错误解析结果, 导致正确结果因迟到被丢弃; 凡是对在黑名单上的IP的请求都会被抢先用TCP RST包重置, 导致TCP连接断开。
基本防御手段: 代理。通过攻击范围外不受影响的主机协助转发请求的方式绕过攻击。
IP黑名单的方法简单有效但有实际应用上的困难, 导致使用本地DNS可短暂绕过。具体来说, 困难有: 应当列入黑名单的IP数量众多、服务对应IP会发生变动、没有便捷的方法掌握黑名单服务的所有IP、IPv6配套设备和工具不到位等。Go Hosts和IPv6隧道方案一度流行就是这种困难的具体体现。
最初使用的代理包括HTTP代理、Socks代理、基于Web的代理(也称为在线代理)和VPN技术。常用的VPN有IPsec、L2TP、PPTP、OpenVPN、WireGuard等。但这四类代理都不是为规避攻击而生的。这些代理使得黑名单中的域名或IP不再直接出现在数据包外层, 但前三种易于通过拓展DPI的检查范围发现, VPN技术具有重要的合法应用且安全性很高, 但两个原因使其在防御上无法起到有效的作用。第一个原因是, 通过包头和握手过程等易于被动发现活跃的VPN连接; 第二个原因是, 在某些攻击范围中, 使用VPN必须登记备案, 直接阻断未知VPN连接不会带来附带损害。这四种代理还有一些缺陷, 例如HTTP代理不能代理UDP流量、VPN只能全局代理导致内部网站访问慢的同时暴露内部站点和自己在使用的VPN服务器地址[3]、在线代理不能保护用户的机密数据等。
通过拓展DPI技术检查范围, 上面提到的四种代理方法都很容易被封锁, 且任何基于域名的非法服务都不再能够稳定存续。例如, HTTP Header中的Host字段、HTTPS握手过程中的SNI字段和TLSv1.3的ServerName字段会被检查。尽管使用DPI技术进行大范围的攻击需要很多资源, 检查范围仍有可能包括每一个出口连接。剩余还有两种方法, 一是经常更换域名或添加新域名; 二是使用代理。前一条需要服务提供者经常维护, 后一条需要访问者主动使用。
发展至此, 后续相关技术对抗才正式进入本文讨论范围内。总结历史上出现过的防御技术原理, 大致可分为八种, 端到端(End to End)、端到中间(End to Middle)、分布式(Distributed)、中心化(Centralized)、中继(Relay)、TLS相关(TLS Related)、侧信道(Side Channel)、隐写(Steganography)。
端到端是大多数代理使用的技术, 指的是通过IP或域名直连代理服务端的情形。这就需要某种代理协议, 可以分为使用公共协议和使用私有协议两种。公共协议的使用又分为使用公共实现的和使用私有实现的。通常配合HTTP代理协议或Socks5协议作为对外服务接口。除了端到中间、中继和分布式外, 都是端到端的。
端到中间是一种需要把配合的服务端置于骨干网中或攻击范围外的ISP核心路由线路上的方法[4]。这种方法具有几个独特的性质: 不需用户部署、不通过访问特定网络地址与服务端接触、附带损害巨大。在设计中, 用户可通过访问目标ISP范围内任意未被封锁的TLS主机并在请求中包含隐藏的标签来尝试接触服务端。如果用户的请求报文恰经过部署了服务端的路由, 路由就会以一种隐蔽的方式声明自己的存在并劫持该连接到代理目标(有时要求该代理目标也支持TLS连接[5])。在这一过程中, 攻击者可以通过被动观察发现服务端的存在和大致路由位置, 但无法得知具体网络地址。文献[6]证明, 强有力的攻击者可以通过BGP劫持等技术改变路由路径绕过有端到中间服务端存在的ISP, 但面临巨大的额外网络资源消耗。尽管这种方法的作者设计意图是使大多数ISP安装此服务以达到被规模化网络劫持的地区因为不得不承受无法承受的附带损害而被迫放弃攻击, 但没有哪个ISP愿意在自己的网络设备上安装这样的设备[7]。此外, 端到中间技术要求用户分享机密信息以便利用已建立的TLS信道, 这在有些情况下是不可接受的。端到中间的代表有: Telex、Decoy Routing、Crrippede、TapDance、MultiFlow、Waterfall。TapDance是第一个旁路版本的端到中间方案, 并使用了新颖的选择密文隐写技术[7]。MultiFlow是TapDance的通信解耦版本[5]。Waterfall是第一个抵抗重路由攻击的端到中间方案[8]。
分布式可以分为中心辅助的分布式和全分布式。前者连接速度快, 性能高, 连接可靠性好, 但依赖于辅助中心服务器。后者连接速度慢, 整体网络性能低下, 有时无法连接, 但不依赖于中心服务器。前者的代表是Tor, 后者的代表是i2p。理论上全分布式还可分为受管理的和自组织的, 可参照P2P协议ed2k和BT。由于全分布式网络面对攻击时(要求连接攻击范围内外的节点而非任意两个节点, 但攻击范围外节点少且发现困难)在连接速度和可靠性上较为脆弱, 目前还没有成熟的方案。为达到通信解耦的目的, 一些方案中会把协议的一部分做成分布式的[9-10]。
中心化可以分为批量提供的服务和分布式辅助的中心化。前者是最为简单直接的方式, 即一次性提供许多个节点供用户自由选择, 所有用户都是端到端直连节点。后者是一种复杂的相互辅助机制, 以中心化的服务集群为主, 但既可以通过预置的地址来直连也可以通过用户节点来发现、发布、共享新的服务地址, 还可以由用户承担少量的数据转发任务, 典型代表是Lampshade协议[11]。
中继可以分为基于第三方网站中转、基于自建服务中转和基于CDN中转。基于第三方网站中转使用附带损害策略, 主要利用免费公共存储服务和云存储等攻击者通常不会封锁的服务作为通信中介, 典型代表有CloudTransport[10]。基于Web的代理也属于基于第三方网站中转的类型, 但易于发现、可封锁、无附带损害、暴露了用户通信明文给代理服务提供方, 是防御技术发展早期曾经流行的一种方式。基于自建服务中转使用淹没策略和通信解耦策略并避免单点故障, 主要是简单的TCP端口转发, 典型代表为DAIP(Distributed Anti-interference Proxy, 分布式抗干扰代理)[11]。基于CDN中转, 使用附带损害策略, 利用域名前置(Domain fronting)技术访问被禁止的服务。域名前置技术利用了CDN实现上的特性, 把未被封锁的域名写在TLS请求外部, 把实际要访问的写在内部HTTP协议的HOST头上, 则CDN会按照外部标识建立TLS连接并按照内部标识请求内容[9]。这样做等于把明文暴露给了CDN服务提供商, 但既然使用了CDN就意味着已经暴露了, 那么这种暴露或许也是可以接受的。
TLS相关(TLS Related)是利用TLS协议的广泛性和安全特性衍生出的一系列防御技术。主要有三种类型, 分别是域名前置技术、SNI改写、加密数据隐写。域名前置技术前文有述。SNI改写会直接去除SNI拓展(根据RFC的要求, 没有SNI也要继续握手)或利用不同域名同一证书的情形建立连接[12], 实际用途不大。加密数据隐写则是通过某种方式把实际数据隐写在TLS通信过程中, 效率很低, 对抗流量分析也不够有力[1,7,13]
侧信道和隐写通常不会单独使用, 而是嵌入在其他方案的某一步骤用于临时传递少量信息。例如端到中间系列方案就采用了这种技术。
按照监测技术成本排序从低到高依次为包过滤、无状态被动单包DPI、有状态同连接多包DPI、无状态单包主动探测、有状态单包组主动探测、时 间或空间跨度上同类型流量关联分析、短跨度内特定类型组流量关联分析、网络连接元数据聚合分析[1,14-16]。需要消耗更多资源的方法在没有强制性需求的情况下, 短时间应该不会实际使用。
从攻击者视角来看, 所有的监测对象只有四种, 可被动发现的、可主动发现的、难以发现或假阳性概率高的、无法发现或正常的。前两种发现后封锁, 第三种在特殊时期或特殊情况下(损害和收益的平衡点升高)封锁, 第四种无法或不需处理。
现在从技术角度具体看一下特定主题下的特殊情况和处理方式。
可被动发现特征的代表有各类VPN协议、原始Tor、Socks5等, 特点是有明显特征, 可直接发现阻断。抗被动探测代理的代表是Obfs2和Obfs3, 被动只能观察到随机数据, 但主动探针可以得到明确的反馈[17]。实际工作的一般模式是先分析流量特点, 如果怀疑程度达到阈值就发送探针进行确认[17]。
随之发展出了抗主动探测的代理协议, 其特点是无协议头、首包验证、无握手或带外握手、对不通过验证的请求不响应内容。典型代表有Obfs4、Shadowsocks、Lampshade、Obfuscated SSH。
以Shadowsocks为例, 从最初发明到达到抗主动探测的水平, 经历了一些曲折。安全缺陷由以下几点引发: 没有防重放机制、没有完整性保护、使用流密码、服务端根据特定字节的值是否合法来决定是否马上断开连接。没有防重放机制意味着可反复发送某个数据包、没有完整性保护意味着可以篡改内容、使用流密码意味着可以精确修改特定位置的字节、服务端的行为保证了只需暴力尝试即可。随后Shadowsocks加入了随机延时抵抗和称为OTA(One-Time-Auth, 一次性验证)的机制, 但由于几乎同样的原因导致仍然可以探测。之后引入了块密码, 但由于没有完整性保护导致了重定向攻击的可能性[18]。此后又引入了AEAD加密方法, 但由于交互过程属于0-RTT类型, 重放攻击始终存在。最终现有活跃版本引入了布隆过滤器来缓解重放攻击的威胁。
由于对探针没有回应, 看似无法发现, 但实际上除寻找设计缺陷外仍可通过七种指标的综合判断以很低的假阳性率判定[17]。七种指标具体为超时时间、触发特定关闭连接方式的字节数阈值(操作系统缓存中有未读取数据则以FIN包关闭, 否则以RST包关闭)、包头部字节熵值(无明文协议头在互联网中是罕见的[17])、流量突发(原本分布在多个主机中的资源现全部通过代理请求导致流量突增)、流量集中(原本指向不同网站的请求现全部指向代理)、泄露的非法DNS请求、未关联的DNS请求(仅本地解析而无实际访问, 实则通过代理访问)。其中后四种亦可在其他代理的流量分析中发挥作用。但在实际工作中似乎遇到了其他困难, 此类代理仍以相当高的概率存活。
隧道代理是一种将数据嵌入到另外一种协议中的代理方法, 外部协议必须是加密协议, 因此SSH、TLS都可以被利用, 但实时视频流更加受设计者欢迎。典型代表有, Trojan、OpenSSH的端口转发工作模式、DeltaShaper、CovertCast、Facet等[13,19-20]。前两种额外消耗较小, 后三种利用的是视频流, 额外消耗大, 吞吐量较低, 但噪声更大。隧道代理在实际攻击中较为困难, 以Facet为例, 使用最先进的攻击方法要封锁90%的Facet流量需要封锁全部Skype视频通话流量的约40%[20]。
模拟代理是一种用第三方实现模拟某著名协议的代理方法。典型代表有SkypeMorph、StegoTorus、CensorSpoofer等。文献[21]中提到, 由于必须实现目标协议的细节、统计特性、实现怪癖和甚至实现错误, 模拟代理是不可行的。但我们认为如果该协议具有泛用性、全加密、明文部分很少、是公有协议, 则是可行的。
中转代理是借由云存储服务、社交平台等可访问的数据中转点进行数据中转的代理方法。使用过程中上传下载数据均使用平台提供的标准方法, 因此不能在网络连接级别区分, 但出于附带损害的考虑也不能简单封锁整个服务。此种方法的性能接近Tor网络, 如果过程中涉及隐写则会进一步降低[10]。使用这种方法的很少, 例子有CloudTransport、PSTA等[10,16]。攻击难度较大。
端到中间前文已经介绍过, 容易发现但绕过或封锁的代价较大。较好的方式是通过技术以外的手段迫使服务提供者放弃承受继续提供服务可能带来的附带损害。现实当中还没有见到这种技术的实际应用。
TLS相关前文也介绍过, SNI相关的兼容性不好, 域名前置技术依赖于CDN但很多CDN都屏蔽了域名前置这种做法[22]。剩余正常工作的仍然是难以攻击的, 且攻击的价值也不大。
一个名为“西厢计划”的项目以文献[23]中逃避IPS系统的相关理论为依据, 致力于寻找拦截机制实现上的漏洞来绕过攻击。基本原理是故意在通信开始时包含重置包并忽略符合某种特征的响应包。具体做法有两种: 一, 故意发送不正确的数据包使得正常的TCP握手中夹带RST包, 以此使攻击程序认为连接已经断开; 二, 依靠侦察收集的DNS投毒的拦截响应, 归纳特征并忽略这些符合特征的响应, 进而使得真实回复有效。这类做法短期是有效的, 但容易失效且局限性很大。
还有一类有政治背景的工具, 依靠资金支持购买大量域名和IP, 依靠普通代理技术和动态更换代理网络地址的方式强行对耗。这些工具不仅不具有技术上的重大意义, 部分还有巨大安全漏洞和窥探用户隐私的行为[24], 在此略过。
另一股技术方向是引入许多新协议, 包括HTTP、HTTP2、UDP、mKCP、MTProto、WebSocket等。混淆方式除了直接做新协议隧道之外。还有许多组合用法, 例如TCP + TLS + Web、TCP + TLS 分流器、WebSocket + TLS + Web、HTTP/2+TLS+WEB等。在具体用法上, 有使用公共实现的也有重新自行实现的, 还有一类是只在数据包前后添加一些字节作为特征的。除了使用公共实现的方案, 都是容易通过流量分析识别的, 引入这些协议除了使花样变多之外没有什么实际意义[22]。事实上, 现实很快证明了这一点。
这一阶段的典型代表是Shadowsocks的混淆插件、V2Ray和Trojan。那些混淆插件只有效了很短一段时间, 没什么理论创新。V2Ray可以通过配置和第三方软件搭配实现种类繁多的层层套壳和中转, Trojan则是对TCP + TLS + Web的专门实现。从外部看来, 基于TLS隧道可利用TLS的全部安全特性, 再加上失败转发机制, 实际运行效果很不错。缺点有两个: 运行沉重, 配置繁琐。
机器学习在加密代理流量识别中是个常见策略, 但仍存在许多问题使得机器学习方法不够实用, 例如数据来源不合理、分类标准不合理、训练结果迁移性不好、运行成本高昂、最好的准确率仅约 94% 且只能在目标网络上训练、假阳性率过高无法承受等[25-32]。
另有几种方法不在上述框架中。例如文献[33]突破了混淆协议本身一并处理混淆、会话和数据传递的惯性思维, 为代理连接添加额外的会话层, 将代理协议划分为用户数据层、会话层和混淆层, 从而大大降低重复建立连接的开销并增加了在多个或多次连接中用户通信不中断的能力。文献[34]提出在TLS隧道中建立Websocket连接, 可传递任意二进制数据, 从而可承载任意代理协议。若未来CDN技术改共享私钥模式为代理信任链模式, 则基于CDN转发的代理技术或可焕发新生[35]。
本方法的设计初衷是提供一种兼具安全性和性能的代理。特点有: 在机密性和完整性的基础上加入隐蔽性、避免重复加密导致的性能问题、为主机被入侵和密钥泄漏两种情形提供更多保护。
据上文所述, 要做到规避网络攻击, 有六大策略和七大技术类别, 但由于前文所述的种种原因实际活跃的高性能选择只有一种搭配, 而这种搭配只有三种思路:
(1) 提供一个通用架构和最高的灵活性;
(2) 提供一个快速简单无特征的加密代理;
(3) 嵌套一个常见且不能简单禁止的协议, 并使两者理论上不可区分。
造成这种现象的原因有许多, 其中最重要的是威胁模型的选择、性能和初始部署难度。有些方法选择了信任提供服务的第三方这样一个不是很强的模型。有些方法为了实现完善的隐蔽性和匿名性过多牺牲了网络性能, 多数情况是不可忍受的。还有一些方法提供了额外的安全特性和可用性保证, 但是初始部署很复杂或不可能独自完成。
本方法选择的是思路三的一种折衷, 是文献[34]的一种特化实现, 在不牺牲性能和部署难度的前提下提供了尽可能多的安全特性, 易于通过拓展部署获得更多可用性和安全特性。包括主动探测对抗、负载均衡、反向代理、流量转发等在内的其他功能借由真正的nginx服务支持。当与nginx协同工作时, 只做简单的转发工作, 可实现理论上的最低性能损耗。
在本方法中, 代理连接建立之初携带完整TLSv1.3握手特征, 握手协议参数嵌入TLS握手中的随机区域和加密区域(例如Random、Session ID、Application Data数据段等), 随后借助Key Exchange扩展完成基于X25519的ECDHE密钥交换和挑战响应过程; 建立会话后对明文数据和特征数据加密, 对加密数据直接转发; 本地配置文件填充到固定长度后使用主密码导出密钥加密; 随机生成用于前期认证的长期密钥; 自动生成并加密保存配置文件, 无需手动传参。更详细的过程在下一节进行描述。
尽管文献[23]宣称, 模拟另一个协议, 尤其是另一个私有协议的某特定实现是困难的和实际不可能的, 但TLSv1.3使我们只需实现TLS协议的极小一部分且可参考工业标准实现源码。经过仔细设计使攻击者不能控制明文且多数情况可以利用真实实现, 进一步降低了模仿要求。通过充分利用nginx的优秀实现和细节特征, 可以避免处理不遵守协议的意外情况。最终, 只需自行处理Client Hello和对加密数据报文的破坏即可。
Fake TLS Socks协议分为三部分: 简化Socks5协议(Fake TLS Socks Protocol, 简称FTLSS)、伪装的TLS握手协议(Fake TLS Handshake Protocol, 简称FTLSH)、传输协议(Fake TLS Transport Protocol, 简称FTLST)。为方便讨论, 下面也称FTLSocks客户端为Socks5服务器。
FTLSS协议只负责FTLSocks客户端与网络应用之间的通信过程, 后续使用FTLSH协议进行双向认证并产生会话密钥, 完成握手之后由FTLST协议负责转发过程。代理流量经过选择性加密后嵌入Application Data数据包的加密部分。加密和解密的过程会在本地维护一个递增的序列号作为AEAD的Additional Data输入。整个通信过程中根据具体情况FTLSocks服务端可能向FTLSocks客户端反馈五种不同的TLS警告消息。
通信过程中各实体间的关系如图1所示。若忽略模板部分, 整个过程有效数据如图2所示。
图1 实体关系图 Figure 1 Entity Relationship Diagram
图2 协议通信概览序列图 Figure 2 Protocol General View Sequence Diagram
基于本协议的代理通信系统部署需要前置要求如下, 否则无法达到设计安全性:
(1) 一个受控域名
(2) 一个合法的证书(可选)
(3) 一台专用VPS
本协议的安全性建立在以下假设的基础上:
敌手拥有窃听和主动攻击的能力但不能遮断信道, 也不能直接获取会话密钥或长期密钥。合法的客户端和服务端预共享一个长期密钥。用户可短期建立与攻击范围外主机之间的安全信道用于初始部署和分发密钥。本地主机和自行部署的代理服务器是安全运行环境, 云服务提供商不会泄露网络通信数据。
下文3.3到3.5节分别详述FTLS协议的三个子协议, 即FTLSS、FTLSH和FTLST。
FTLSS协议用于在客户端提供基本的Socks5服务。Socks5 代理协议被广泛支持、简单易用、同时支持 TCP 和 UDP, 但对于我们的特定需求而言仍可进一步简化, 只保留必要的特性。简单来说, 这里只包含Socks5的无认证模式和TCP Connect指令。这部分内容发生在Socks客户端和Socks服务端之间, 对应图3的①。
图3 通信序列图 Figure 3 Comunication Sequence Diagram
握手阶段: Socks5服务器在收到客户端的协商请求后, 会检查是否包含无认证方式, 返回给客户端的值只有两种可能:
· ‘0x05 0x00’: 采用无认证的方式建立连接
· ‘0x05 0xff’: 任意一种认证方式都不支持
建立连接: Socks客户端发出指令, Socks服务端收到后建立一条与FTLSocks服务端之间的新连接。这里总是回复: ‘0x05 0x00 0x00 0x01 0x00 0x00 0x00 0x00 0x10 0x10’表示接受代理请求。客户端收到这个请求后会发送第一个原始请求数据包, 即图3中的Data1。FTLS客户端此时表现与标准Socks5服务器不同, Socks指令与第一个请求数据包被合并为一个数据包后加密处理再转发, 即图3的②。
FTLSH协议用于建立安全会话。为理解FTLSH的设计思路, 先看在 TLSv1.3 握手中的3 个阶段:
密钥交换: 建立共享密钥数据并选择密码参数, 之后所有的数据都被加密。
Server 参数: 建立其他的握手参数。
认证: 认证 Server(并且选择性认证 Client), 提供密钥确认和完整性。
为达到伪装目的, 我们只需关注第一个阶段即可。取TLSv1.3用于建立HTTPS连接时的惯用参数, 将FTLSH所需参数插入其中。具体以使用Firefox 71访问全面支持TLSv1.3的某知名网站取得的数据作为模板。伪装方法如图2所示, 对应图3的③, 具体描述如下:
(1) 客户端与服务端之间先以四个数据包作为开端, 依次为客户端发送Client Hello, 服务端发送Server Hello, Change Cipher Spec, Application Data, 客户端发送Change Cipher Spec, Application Data, 随后双方均为Application Data, 除非连接出现严重错误。
(2) 整个握手过程中, 若一切正常不应该出现任何警告消息。若遇到非常严重的网络错误或有攻击者插入恶意流量则所有四种致命警告都有可能被触发。错误与警告消息的对应关系遵守RFC 8446的规定。
(3) 未加密的Client Hello和Server Hello消息以模板填充的方式进行构造, 此后从Application Data开始即为加密通信。
(4) Change Cipher Spec和Alert是固定内容, 可以在合适的时候直接发送, 发送之前不需要特殊处理。
(5) Client Hello和Server Hello都有Random和Session ID共64字节空间。可供传输一个HMAC- SHA256((timestamp, all remain data), PSK)值作为Session ID。发送32字节的随机值作为挑战值填充到Random。其中timestamp容许误差范围为[-1,0]。
(6) 双方利用Key Exchange字段使用X25519进行ECDHE密钥交换。
(7) 服务端和客户端分别进行身份验证。前者验SessionID == HMAC ((timestamp, all remain data), PSK), 后者验证 First_AD_Payload == HMAC (all_remain_data, PSK) && response == HMAC (challenge,session_key), 均通过则建立连接。
(8) 对第一条消息, 客户端验证失败则直接断开连接, 目的在于辨别是否有恶意Server。服务端验证失败即把数据包转发给nginx并回传nginx的响应, 目的在于辨别是否有恶意Client。若服务端验证失败且连接后端伪装服务失败, 则向对端发送Close Notify Alert并马上关闭TCP连接(配置一个可用的伪装服务是必要的)。
(9) 对第二条消息, 客户端验证失败则直接断开连接(说明出现尝试攻击的Server但PSK尚未泄露), 服务端验证失败(说明第一条消息被成功重放)则发送BadRecordMAC并马上关闭TCP连接。
需要特别说明的是握手包的大小问题:
(1) 根据RFC 8446要求, 不足512字节的Client Hello应该填充到512字节, 所以对本模板中域名长度不超过160个字符的情况, Client Hello固定为512字节长度。为了简化协议提高速度, 不对超过160个字符的超长域名提供支持。
(2) 假设同一个HTTPS服务器对同样的Client Hello总是回复同样的Server Hello, 则Server Hello也是固定长度。
(3) Server Hello之后会有一个长度为32字节的Application Data包用于完成后续TLS握手。
(4) 这些包长度不变是符合规范要求的。并非所有采用了TLSv1.3的网站的握手过程都是这样的, 但这种方式是一种被推荐的和流行的方式。
FTLST协议用于提供转发和加密流量分流特 性。有两种路径进入FTLST阶段。第一种路径是探测到第一个握手包非法, 此时被判定为非法的访问流量不经任何处理直接由FTLSocks服务端转发到伪装出口; 第二种路径是正确交互后正常提供服务。
第二种路径具体可分为两种情况: 首先, 客户端和服务端分别独立运行分流算法, 通过记录握手后的前两个包是否是TLS协议的Client Hello和Server Hello来判断当前代理连接是否是TLS连接。若为TLS连接, 针对之后通信中的Application Data类型数据包, FTLSocks客户端直接转发, 服务端通过试解密来判断当前数据包是经客户端处理后得到的Application Data还是原始Application Data。针对其他类型的数据包, 客户端不需判断, 直接加密后嵌入Application Data数据域转发。若非TLS连接, 客户端对后续所有数据包直接加密; 服务端解密后转发到代理目标。
即依据前面连接的状态决定可能采取三种不同的转发策略, 其思路主要有两点: 不重复加密、不对TLS协议进行实际处理。对应图3中的④~⑨。
不重复加密: 分流策略如图4所示。“不重复加密”降低了两端的计算成本, 有限算力下可以提供更高的流量处理能力。非加密流量处理过程如图5所示, 加密流量处理过程如图6所示。
图5 非加密通信示意图 Figure 5 Non-encrypted Communication
图6 加密通信示意图 Figure 6 Encrypted Communication
不实际处理TLS: “不实际处理TLS”在减少服务器计算的同时为更低的响应时间带来了可能性。相比于完整的TLS协议复杂的处理过程, FTLST仅包含加密流量分流、数据包序号滚动和五种警告消息。每次成功加密序列号递增一, 同时12字节随机值作为nonce随加密数据传输。后续4.2.3和4.4.1节还会详述。
下文分别分析FTLS协议面对窃听攻击(通过监听通信过程获取机密信息)、中间人攻击(分别欺骗双方使其各自认为攻击者是对方)、重放攻击(重新发送监听得到的数据以获取信任或造成未授权的操作)、数据篡改(截获通信数据后修改再发送给原接收方)、主动攻击(攻击者假装是通信的某一方与另一方交互, 然后利用交互过程得到的信息进一步进行攻击)的安全性, 并探讨密钥泄露情形下的安全性。最后, 对攻击者利用流量分析技术(分析网络流量的长度、内容和特点等来识别特定类型的网络活动)识别伪装的可能性和对策进行了简要说明。
主密钥长度256bit, 使用CSPRNG生成。采用ECDHE机制交换密钥, AEAD加密处理, 可保证抵抗窃听攻击。
握手过程中包含一次双向挑战响应, 双方都必须同时使用PSK和当次协商的会话密钥证明身份。对服务端来说, PSK用于生成HMAC, 会话密钥第一次用于计算响应; 对客户端来说, PSK用于生成HMAC, 会话密钥第一次用于请求数据加密。所以中间人攻击也是不可行的。
一般来说, 正常的真实TLS握手(非0-RTT且非恢复会话)中的Client Hello和Server Hello不包含机密信息且即使重放也不能正常建立TLS会话, 不必担心重放攻击。但FTLS协议把这两个数据包作为身份验证信息的来源, 所以必须充分考虑重放带来的影响。
4.2.1 Client Hello
使用时间戳的HMAC-SHA256值作为防重放的基本措施, 有效时间2 s。可以看到Client Hello是有可能重放成功的。但由于后面的第二轮交互之前随即完成了密钥交换, 之后的会话需要使用会话密钥进行验证和加密, 进行重放的攻击者不能计算出正确的会话密钥, 所以不可能再次发送合法的数据包, 所以重放不可能成功, 第二个包必定错误并被BadRecordMAC类型的Alert消息拒绝。攻击者能够重放Client Hello并不会带来实际风险, 因为其即使重放成功也不会得到与正常服务器不同的反馈。
4.2.2 Server Hello
由于Server Hello本身具有的性质, 不可能被重放。Server Hello中包含了一个HMAC值保护其完整性, 同时其中填充的Session ID必须与对应Client Hello一致, 不破坏完整性的同时保持Session ID一致对实施重放的攻击者来说是不可能做到的。
4.2.3 其后任意数据包
其后的每个数据包都涉及AEAD加密。防止重放的关键在nonce和序列号的使用。nonce取12字节随机值并随加密载荷传输; 序列号不传输, 作为AEAD的Additional Data在本地递增, 每成功加密一次或解密一次递增一。重放、截断、篡改、重排序都将导致AEAD验证失败(BadRecordMAC Alert)并中断连接, 必须重新握手才能恢复通信。
与上一节中描述的类似, TLS中的Client Hello和Server Hello也不必担心篡改, 但在FTLS协议中为了简化对TLS握手过程的模拟并避免对异常握手过程的处理, 必须对两者进行完整性保护。完成密钥交换前的第一轮交互两个数据包使用HMAC保护完整性, 完成密钥交换后使用AEAD保护完整性, 非加密数据部分是固定值并被检查。综上, 篡改是可以立即发现的。
若验证失败说明对方未遵循FTLS协议, 此时交由nginx进程处理收到的握手包可保证对异常握手数据包正确响应。因此, 攻击者不能通过构造特殊握手包来辨别当前是否为FTLS服务。
从发起连接到握手完成, 许多步骤都可能遭遇主动攻击, 接下来分别做出分析。由于协议本身的性质, 被察觉与正常TLS服务的不同即丧失隐蔽性可认为是“没有达到设计目标”。
4.4.1 主动探测
由于众所周知的原因, 先来谈主动探测的应对措施。主动探测指攻击者伪装为客户端主动向可疑服务端发送探针, 并通过响应判断可疑服务是否为特定目标服务的过程和手段。
失败转发: 针对握手过程, 目标是使探测者或任意未持有正确访问凭证的访问者无法通过直接访问觉察活跃的FTLS代理服务与TLS服务的差异。
具体来说, 第一个握手包(Client Hello)验证失败则不经任何处理直接转发到伪装服务; 若第一个握手包验证成功(必定是有效时间内被重放成功)则来到第二个握手包的验证, 但任意非法对端由于无法计算出正确的会话密钥不可能验证通过。一旦验证失败, 则按照TLS协议规定发送BadRecordMAC消息后关闭TCP连接。这里关闭连接的动作不会牺牲隐蔽性, 因为任何一个HTTPS服务收到不符合协议的数据包时都是这样处理的。
检测并关闭遇到攻击的连接: 为了不在主动攻击下显示出与常规TLS服务的区别, 需要在连接发生错误的时候给出恰当的警告消息并正确处理是继续连接还是断开连接。根据前面的分析, 攻击者不可能直接与代理服务进行可控明文的合法交互, 所以一旦插入攻击探针必定导致致命错误。可能的情形共有五种, 分别为篡改握手、篡改明文协议头、篡改密文、重放握手、重放Application Data和重排序。对应的警告消息只能是四种会导致TLS连接断开的致命错误警告Illegal_parameter、BadRecordMAC、UnexpectedMessage和RecordOverflow之一。为应对这种探测, 只需根据恶意攻击探针插入的时间和方式的不同, 恰当地给出警告并关闭连接即可。
收到致命错误警告时关闭连接: 虽然攻击者不能注入合理的密文, 却有可能注入明文的致命错误警告消息。根据RFC文档的规定, 收到致命错误警告时应该关闭连接, 则此时遵守协议关闭连接即可。
4.4.2 非法客户端
假设攻击者没有得到长期密钥, 则当攻击者伪装为客户端与服务端通信时, 会由于缺失有效的预共享长期密钥, 无法正确构造第一个包, 不能伪装成功。握手验证失败后, 攻击者被指向伪装服务, 无法得到任何有效信息。
4.4.3 非法服务端
当攻击者伪装为服务端与客户端通信时, 由于缺失有效的预共享长期密钥,不能正确计算Server Hello的MAC值, 所以不能伪装成功。失败后客户端不会继续握手而是发送UserCanceled后发送CloseNotify, 随之关闭连接。攻击者得到了一个有效的Client Hello但既不能重放也不能得到长期密钥, 无助于进一步攻击。
这里客户端的行为与正常的客户端不同, 可能会被发觉异常, 但正常客户端也偶尔直接关闭连接。一段时间的静默可能有助于避免暴露客户端的存在, 但考虑到对客户端IP的动态性以及对服务可用性的巨大影响, 并未实现此主动静默的功能。
4.4.4 传输阶段
传输阶段本质上是TLS头与AEAD加密数据的组合。TLS头检测为非法时发送UnexpectedMessage警告。若被插入巨大数据帧或篡改长度字段为超长则发送RecordOverflow。篡改和重放的情形前面已经覆盖, 不再重复。
也许有些防御人员相比通信内容的机密性更注重连接的可用性, 但如果在不同程度的密钥泄露场景中都能够提供额外的保护对很多场景是非常有吸引力的。
下面分别叙述两种不同场景下对代理使用者和代理数据的额外保护措施。
4.5.1 会话密钥泄露
由于软件缺陷, 可能导致内存内容泄露进而泄露当前会话密钥。会话密钥是通过ECDH协商而来, 并且只用于当前连接, 因而具有前向安全性。此时对任意其他的会话、此前的会话和主密钥都不能构成任何威胁。
4.5.2 本地入侵
如果被入侵, 则存储在本地的配置文件包含的机密信息可能被泄露, 进而导致长期密钥泄露。采用了五种措施来应对这种场景:
(1) 创建配置文件时时强制使用较长和较复杂的主密码, 防止暴力破解。
(2) 使用参数为time=100, memory=32*1024, threads=4, keylen=32的Argon2id算法从主密码导出主密钥来加密配置文件。密钥导出过程加盐, 长度32字节, 每次生成配置文件的时候随机生成并附在配置文件末尾, 防止暴力破解。Argon2id算法的推荐参数中memory=64*1024, 我们使用的是推荐参数的一半, 但把参数time扩大到了推荐值的100倍, 总体安全性并没有降低。同时因为只在开启时运行一次所以不会对性能表现造成消极影响。参考消耗时间: 926.12ms on Intel Core i5-8300H。
(3) 不从命令行参数接受主密码, 而是以无回显的方式在后续步骤手动输入, 防止在命令历史中泄露密码。
(4) 载入配置文件后立即覆盖内存中的主密码。
(5) 生成配置文件时分为两个部分, 客户端配置文件不包含服务端参数(伪装出口地址, 超时时间, 协程池大小等)。两个配置文件主密码相同、大小相同, 但盐值不同。
(6) 配置文件加密前先填充到固定长度, 保证不会因为文件大小泄露关于配置的任何信息, 例如域名长度。
综上可以得出结论, 即使客户端或服务端被恶意用户取得本地访问权限也不大可能获得长期密钥或利用本工具留下的信息帮助寻找服务端、解密通信内容。
超时时间、连接持续时间和包长度分布等是流量分析的主要内容。其中超时时间是可配置的, 后面两者取决于通过代理的连接。超时时间默认值60 s是互联网中普遍的选择但也可以根据需要修改。
后面两个因素若不加额外处理(这正是我们选择的做法, 更加激进的隐蔽性会牺牲大量性能)则直接反映了代理服务的使用情况且可与我们所声称的服务可能提供的流量作对比来判断是否足够可疑。但这既没有让FTLSocks相比现存的稳定工具更可疑, 也可以通过选择更合理的伪装服务来减少可疑的程度。实际上, 即使加入随机字节填充也不会让包长度分布看起来更加正常。
综上, 安全性分析大致可分为四种情况: 第一个包被发现攻击; 第一个包重放成功, 第二步被合理拒绝或超时断开; 被动观察无区别; 主动探查被合理响应并断开。不可观察性的要点在于: 从头到尾彻底的完整性保护确保了交互在约定的框架内进行而无需考虑过多细节, 只需关注代理功能; 超出框架外的由TLS的真实公共实现应对。
下文从安全性和性能两个方面与各个流行软件比较, 分析FTLS协议的优势。前向安全性、性能、单点故障抵抗和部署简单是FTLSocks的突出优势。
下面与较为流行的几个同策略工具和较具有代表性的其他工具进行分析对比。
V2Ray: 是一个通用工具集, 也是一个通用的网络通信框架, 提供了多样的协议支持和功能特性, 具有很强的可定制性。通过修改配置文件可以实现非常丰富的功能, 也可以使得外部流量具有各种形态, 还具有内置的路由和DNS, 稍作更改就可以实现大部分新的混淆方案。官方发布的核心工具集由go语言实现, 但同时存在很多第三方实现。其缺点在于当定制多层通信协议的时候, 额外开销较大, 有时存在双重三重加密的情况。整体而言, V2Ray选择的是大而全的路线, 新方案可以最大程度上受益于内置DNS、连接复用、分流引擎等公共模块, 使得新方案也可以立刻得到较好的用户体验。
表1 特性对比 Table 1 Feature Comparison
Trojan: 完全以使用HTTPS流量发送一个类Socks代理协议来实现网络攻击规避为目标的工具。其实质是, 以“收到的数据包能否解析为合法协议”为规则进行流量分发的反向代理服务器。官方发布的工具以C++实现, 使用OpenSSL 1.1.1d作为HTTPS的实现。其缺点在于真实建立的TLS连接开销较大, 新连接响应速度较慢, 存在对大量HTTPS流量的重复加密、不能放在反向代理后做负载均衡。若采用本地运行一套完整网站的方法来提供主动检测对抗基础措施对服务器是个不小的压力, 不利于大规模部署。若采用静态或不更新内容的动态网站则失败转发机制失去了意义, 固定的内容使得容易被怀疑和判断。其也未提供便捷安全的配置方式, 只能直接编辑明文的json文件。其目前(此处指1.14.0版本)的实现有几个缺陷: 无良好的默认密码策略、对不合法请求的处理较慢、多核计算能力较弱、反向代理功能不完善(特别的, 不能反向代理HTTPS)。Trojan抛弃了V2Ray的臃肿架构, 将TLS嵌套模式单独取出再对TLS握手过程第一步进行修改。配合首包验证和失败转发, 最终得到了更加隐蔽高效的代理模式。但同时也失去了反向代理、多级代理、负载均衡、单点故障抵抗等诸多特性和网络架构上的灵活性。
Shadowsocks: 一个功能简单, 目标单一, 快速的加密Socks代理。其传输一个类Socks协议但把传统的Socks服务器拆分成客户端和服务端, 两端之间加密处理。服务端收到数据后进行试解密, 解密后协议解析成功则执行功能, 否则什么也不做。除此之外还可以在转发之前根据访问地址进行分流。支持混淆插件为流量进行预处理以便添加额外特征, 但时至今日受限于插件的工作方式这些混淆只是聊胜于无。Shadowsocks无官方版本, 目前流行的有C语言实现的 shdowsocks-libev、go语言实现的shadowsocks-go2和停留在08/10/2015的2.8.2版本的Python实现。各种实现的功能特性不一, 但得益于Shadowsocks的设计思想始终保持快速的处理能力和无明显特征的流量。其优点在于简单快速, 其缺点在于过于简单粗暴容易引起怀疑。在2.4节中, 我们介绍了此类代理的弱点和探测方法, 但不得不承认这种方法仍然有着一定的生存空间和其他方法难以比拟的性能优势。
Tor with Obfs4: 这是一种隐蔽地连接Tor网桥的方式, 是连接到Tor网络的一种方法。这里我们关注的重点是Obfs4。它和Shadowsocks一样属于抗主动探测代理, 但专用于连接到Tor网桥。许多防御方式最终都连接到了Tor网络并由Tor网络提供剩余的服务, 充分利用了Tor网络的匿名性。凡事有利有弊, 匿名性与效率是一对无法调和的矛盾, Tor网络特殊的运作方式使得获得更高的性能非常困难。通过一个并不臃肿的工具和不同的使用方式来获取灵活配置的匿名性、安全特性和性能可能是一种更好的方式。
Lantern: 一个跨平台的高可用性商业代理工具。Lantern的协议Lampshade和其运作方式是介绍的几种协议中最为复杂的。其结合了中心化和分布式相关代理技术并由维护者部署的中心服务器提供主要服务, 用户可作为P2P节点提供辅助服务。为了提高性能表现和可用性, Lantern抛弃了匿名性, 但也因此使服务地址更容易被攻击者得知[12]。由于部署的复杂性、运营的商业性、极弱的可配置性, 我们不认为Lantern是一个具有潜力的和值得信赖的工具。
FTLSocks: 对FTLS协议的实现, 由本文作者开发。为了提供一个安全、快速、轻量化、可配置、特性可选择的代理工具而被提出。最初的目标是减少Trojan实现方案带来的负担并提供相似的防御特性。伪装思路是伪TLS隧道代理, 并非完整TLS实现, 只实现了一半假的握手过程和一些警告的处理。结合首包验证机制和失败转发机制, 不能通过验证的连接由nginx处理, 从而节省了TLS相关处理过程的实现并充分利用了nginx的高性能实现和完善的反向代理功能。协议被设计为通过可通过不同部署方式或与第三方软件灵活配合来实现分流、负载均衡、单点故障抵抗等特性, 本身仅在高性能的前提下提供前向安全、机密性等基本安全特性。此外, 提供了更好的默认密钥策略和配置向导。
设计新方法并创建本工具的主要目的是提高性能并避免不必要的计算资源消耗, 因此与流行工具相互比较的一次性能测试是必要的。
表2 系统资源消耗 Table 2 System Resource Consumption
5.2.1 测试方法
测试使用siege进行, 分别使用大中小(147MB/ 22MB/74KB)三种大小的文件和低、中低、中、高(2、5、20、150个并发线程, 分别代表个人轻度使用, 个人重度使用, 多人轻度使用, 多人重度使用)四种并发程度进行持续一分钟, 随机延时0.5s(siege的默认值, 更好地模拟真实访问的分布)的压力测试。
加密方法全选AES-256-GCM, 保护方式可选时选TLS模式。
5.2.2 测试环境
所有测试数据和程序在tmpfs上保存, 确保本机IO不会成为测试瓶颈。为确保硬件规格一致且环境温湿度不会使硬件性能发生漂移导致测量不准确, 测试在云主机的环回网络进行, 云主机的型号为阿里云密集计算型 ic5 / ecs.ic5.2xlarge(8vCPU 8GiB)。
测试的硬件环境为Intel Xeon(Skylake) Platinum 8163 8vCPU 主频2.5 GHz, 睿频2.7 GHz, 8 GB内存, 内网带宽2.5 Gbps, 内网收发包80万PPS。
软件环境为CentOS 8.1 x86_64 Kernel 4.18.0, nginx/1.17.10。
其中nginx采用Mozilla Guideline v5.4推荐中级配置, worker_processes=2, worker_connections=1024, keepalivetimeout=60, sendfile=on。
5.2.3 测试结果
参与测试的软件有 FTLSocks/0.1.0、shadowsocks-go2/0.1.0、shadowsocks-libev/3.3.4.0、Trojan/1.14.0和V2Ray/4.23.1。测试项目包括响应时间(s), 吞吐量(MB/s)、最长请求完成时间(s)、最短请求完成时间(s)。
为节约空间并使图表更具代表性, 吞吐量选取所有测试结果, 最长/短请求时间选取大文件测试结果, 响应时间选取中等文件大小测试结果。除请求完成时间因加密和非加密的测试结果极度相似而只选取加密情形进行展示外, 都同时展示了两种情形。
概览所有性能测试结果图示可以发现, 性能表现从优到劣的大致顺序为直接访问、FTLSocks、shadowsocks-go2、shadowsocks-libev、V2Ray with TLS、Trojan。与预期不同的是Trojan的表现在高并发下低于V2Ray, 低并发下高于V2Ray。
吞吐量的测试中, 高并发情况下小文件较低, 中等文件情形正常, 大文件时轻微下降; 测试压力较低时不同程序差别较小; 随着文件的增大, 即使并发压力较低时也可以看出明显的性能差距。无论哪种情形, FTLSocks与shadowsocks-go2都是代理程序中表现最好的两个, 且远超第三名。
在最长/短请求完成时间和响应时间的测试中, 六张图显示的趋势基本一致, FTLS协议都表现出了优势。横向对比可以看出FTLSocks的代理过程对响应时间的影响最小且高并发下阻滞时间也最短。
比较HTTP和HTTPS通信可以发现, HTTPS通信在所有情形下性能表现都有所降低, 但各被测对象性能表现的相对关系几乎不变, FTLSocks始终占据较大优势。虽然FTLSocks的HTTPS流量转发过程减少了对加密和解密的需求, 但增加了选择判断的需求, 结果上看性能增加可观却不及预期。
图7 性能测试组图 Figure 7 Performance Test Group Chart
比较不同并发情况可以发现, 当并发线程数达到150时, shdowsocks-libev、V2Ray和Trojan的吞吐量都随着并发数上升而下降, 说明触及了并发性能瓶颈。反观FTLSocks和shadowsocks-go2则吞吐量仍有少量上升且FTLSocks的吞吐量上升幅度略大, 充分证明了FTLS协议的性能优势。
5.2.4 系统资源占用比较
这里主要比较客户端数据。如表2所示。服务端一般来说与客户端相差很小, 且计算资源和空间资源相对充足, 为简洁起见不在表2列出。
CPU资源消耗的详细信息如表3-4所示。文件大小的比较方法为去除调试信息和符号表再用UPX压缩处理。对非单个可执行文件的计算所有文件, 数据文件按照7z常规压缩后大小计算。静态链接的只计算可执行文件本身, 动态链接的同时计算动态链接库文件。
内存占用的比较方法为, 分别比较客户端默认配置下启动状态和压测后。用于比较的压测方法为20线程中等文件大小HTTP方式压测一分钟。选择这种方法是因为从性能测试数据来看这种情形下最能充分发挥性能。
CPU占用率的比较方法为, 首先采用20线程中等文件大小压测1 min, 随后统计出平均CPU占用率, 最后分别比较HTTP和HTTPS两种情况下单位数据量消耗的CPU资源。CPU占用率数据使用pidstat工具收集, 吞吐量使用siege工具收集, 数据独立于性能测试环节。计算方法为平均CPU占用率(%)除以吞吐量(GB/s)。考虑到吞吐量更大时加解密必定会消耗更多计算资源, 这种计算方法是比较公平的。
从表中可以看出, FTLSocks在内存占用大小和文件大小上与用C/C++编写的shadowsocks-libev和Trojan或相对逻辑简单的shadowsocks-go2都不占有优势。从CPU占用率可以看出, FTLSocks所需资源相对较少。但从另外一个角度来说, 无论是哪一个都相当小且足够小, 都可以在资源受限的嵌入式设备上良好工作并得到较好的性能表现。
表3 HTTP情形下的CPU消耗 Table 3 CPU Consumption on HTTP
表4 HTTPS情形下的CPU消耗 Table 4 CPU Consumption on HTTPS
可以看出除Trojan的HTTP情形之外雷达图上对称维度差别不大, 这说明客户端和服务端的CPU资源消耗是几乎对称的。需要特别指出的有三点: FTLSocks客户端消耗比服务端略大、V2Ray消耗最大且远超其他几种、客户端与服务端总体持平但Trojan在HTTP情形下用户空间CPU占用较突出。具体如表3~4和图8所示。
图8 CPU消耗雷达图 Figure 8 CPU Consumption Radar Chart
此外, 值得一提的是, 即使反复用大量数据和高并发来测试, FTLSocks也不会占用比仅压测一分钟后更多的内存(多次测试中误差大约为1M)。
5.2.5 分析
让我们结合源代码和性能测试数据来进行具体原因的分析。首先需要说明的是, 本工具用于导出加解密配置文件的主密钥是Argon2id。该算法具备内存消耗机制来抵抗ASIC、GPU和高性能计算集群的攻击。在本实现中选择了32M作为内存参数, 因此可以认为空闲情况下实际消耗内存为2.4M。这部分空间在密钥导出结束后将作为堆空间由go runtime管理。
Shadowsocks的协议结构简单, 且属于预置密钥的0-RTT协议, 各方面超出本工具的表现是理所当然的, 此处仅为提供一个流行工具性能标准的参考。所以分析的重点是Trojan和V2Ray的TLS模式与本工具的比较。
对比本工具和V2Ray, 可以发现本工具的CPU占用率较低, 但性能表现却更好。虽然内存占用相对较高, 但仍在可接受的范围内。分析V2Ray的TLS伪装实现可以发现, 在这种场景下V2Ray进行了两次加密, 完整使用了TLS, 为保持通用性还做了包括路由、DNS、连接复用等许多额外的工作。这就是V2Ray消耗CPU资源较多的原因了。
Trojan虽然在内存占用和CPU占用上具有优势, 但同时性能表现也大大下降了。从性能测试数据可以发现只在并发量较低时Trojan的表现才接近或略高于其他比较对象。
由此也可以看出网络上流传的大部分性能测试之所以得出Trojan性能更高的结论的原因是: 只测试了较低并发负载(单用户单代理目标)下的使用场景。
阅读源代码可以发现, 这种现象本质上是由两个原因造成的:
(1) 对多核计算能力利用较弱;
(2) 完整的TLS栈。
FTLSocks使用了更简化的协议设计, 从根本上保证了更高的吞吐量和更优的性能表现。从测试数据可以看出, 无论是吞吐量还是响应时间抑或最短最长请求完成时间都占有相当大的优势。
在实现上, FTLSocks使用了协程池来增强并发性能, 对象池来减少内存分配, 无竞争无锁的设计, 所以能够充分利用CPU性能, 实现同等压力下更高的吞吐量。缺点就是占用了略多的内存。在长时间大规模的并发场景中, 采用协程池无论是计算资源还是内存资源消耗上都是有优势的。
虽然FTLSocks在单位数据CPU消耗上略有劣势但仍大致处于同等水平, 考虑到提供的丰富安全特性和极佳的性能表现, 应当认为FTLSocks是一种更优的设计。
此外, 由于FTLS协议的特性, 从未建立真正的TLS连接, 因而可以得到两个额外的好处。(1)可以结合nginx的HTTPS反向代理功能利用世界上其他HTTPS网站作为反向代理目标, 节省本地搭建和运行服务的精力和系统资源消耗; (2)可以作为反向代理的后端, 在负载均衡的帮助下实现更优的性能表现和更高的可用性。
快速部署: FTLSocks的最简部署方式只需一个域名和一台服务器, 部署过程只需要生成配置文件、上传和运行三步即可提供代理服务, 合法证书和伪装服务都不是必须的, 但这样做同时也是隐蔽性和可用性最差的方式。通过下面介绍的使用方式可以充分发挥FTLS协议的威力, 极大增强隐蔽性、可用性和安全性。
服务集群: 由于FTLSocks设计上的极简性, 使得规模化部署服务集群非常容易。可以把快速部署的方法简单推广到服务集群的部署中。
快速恢复: 通过组建转发网络, 可以抵抗单点故障并在FTLS服务器被阻断访问时迅速恢复访问。该网络至少需要两台转发器和一台服务器, 较好的结构应该有至少三台转发器和两台服务器, 有条件的情况下转发器越多越好。使用过程中应当始终保持至少一台转发器从不在紧急情况以外使用, 留作恢复访问的紧急入口。该转发网络的大致结构和通信过程如图9所示, URGE连接从不在紧急情况外使用, Relay转发流量到真正的FTLS服务从而在攻击者观察下隐藏代理服务器的IP地址。这种使用方式并不是FTLS协议的一部分, 但协议的设计中考虑了这一部分, 从而保证了转发网络可以顺畅运行。
图9 转发网络拓扑图 Figure 9 Forwarding Network Toplogy Diagram
本地入侵: 通过申请证书、部署伪装服务、使用较高强度的主密码、专用的非特权用户来运行、仅允许非特权用户使用带密码保护的私钥进行SSH登录、不运行其他服务, 可以减少攻击者入侵后从内存得到主密钥的风险。
流量分流: 通过将AutoProxy类似的软件置于FTLSocks前可以起到分流的作用, 进而避免不必要的代理动作, 提升用户体验。
FTLSocks提供的配置向导可以对一些参数进行调节, 这些参数对性能、流量负载能力造成一定影响的同时还会给流量带来一些特定的特征(例如性能下降曲线、超时时间等)。为避免基于流量指纹的流量分析技术的攻击, 可以通过不使用参数默认值进一步增强隐蔽性。此外, 配置向导提供的KeepAlive和Deadline参数值是常用默认值, 可以不改变。
FTLSocks中使用的“首包验证”结合“失败转发”的机制在设计上需要一个伪装服务, 这个服务的选择对隐蔽性有很大的影响。虽然基于TLS的伪装无法在应用层辨别但仍有可能结合具体场景进行区分。为缓解这一问题可以采用使攻击者无法确定流量性质和伪装出特定场景的统计特征两种策略。以前者为例, 通过在代理的伪装出口使用合理的伪装服务(例如, 一个有身份验证的API服务), 可以减少可疑程度。这里的关键点在于, API服务的非授权用户只能得到很短的错误消息, 不能确定后续内容长度和性质的变化规律; 基于Web的服务存在大量静态资源或变化频率很低的动态资源, 而这些资源的大小和访问时机都是可预测的。此外, 长期连接某特定Web服务是可疑的, 但长期使用某API服务则是可以接受的。后者的代表如NaïveProxy, 直接使用Chromium开源项目的网络堆栈模块, 可以获得Chrome浏览器的统计特征。
从攻击的角度来看, 辨别其伪装性最好的办法是使用基于用户画像的方法长时间观察目标网络活动[36], 利用2.2节中提到的代理七种指标进行综合判断。这些指标是代理使用者的网络流量必然出现的特征。有节制地使用代理并对本地网络和伪装服务进行恰当配置可以有效挫败这种尝试。
除代理本身的部署情况以外, 使用的环境和方式也会对隐蔽性有所影响。例如, 在浏览器中使用代理时阻止WebRTC可以防止在你访问某些网站时泄露你的本地IP地址。使用CDN来让代理流量先经过一条合法路径也可以增强隐蔽性但现有CDN要求使用源网站的证书的私钥才能提供服务, 这种退让从根本上说是CDN技术设计的安全缺陷。若采用文献[35]提出的允许CDN提供商和源网站各自独立维护自己的证书和私钥的CDN方案就可以更放心地使用CDN作为代理服务了。
综上, FTLSocks通过配合特定的使用方式可以达到更强的隐蔽性、安全性和可用性。未来的工作中可尝试通过整合Turbo Tunnel思想[33]在复杂网络环境中提供更高可靠性, 并通过恰当填充降低通过侧信道识别[37]的可能性。
FTLSocks通过设计新的伪装协议兼顾了吞吐量、响应时间和安全性, 最终实现了性能大幅提升、安全性大幅提高的同时, CPU、内存资源消耗提高不明显的效果。新协议得到了类似于Trojan的安全性和近似Shadowsocks的性能, 同时避免了Trojan在反向代理上的天然缺陷。FTLS协议作为一种新的伪装代理协议是相比现有流行协议更优的协议。
致 谢 感谢陈嘉耕教授在我写作本文的过程中给予的悉心指导。
注: 文中图形均为作者原创, 使用英文进行文字标注是受空间所限。特此说明。