揭秘QUIC的性能与安全

1,009 阅读20分钟

本文来自OPPO互联网技术团队,转载请注名作者。同时欢迎关注我们的公众号:OPPO_tech,与你分享OPPO前沿互联网技术及活动。

在当代网络通讯中,高速且安全的网络接入服务已为互联网厂商的共同追求。针对传统的TCP + TLS的安全互联网服务,在各大头部互联网厂商中反响激烈,如google有提出各种升级和补丁方案,例如TCP fastopen, TLS1.3等,而基于TCP的传统体系已经有运行几十年,形成了固化甚至僵化的网络基础设施,导致补丁升级或者新方案融入变得非常困难。为了更体系化更自然且原生的解决这个问题,google从UDP的途径另辟蹊径创建性能更好、安全性更高的QUIC(Quick UDP Internet Connection)。IETF也把QUIC作为HTTP3的标准基础通讯设施。

1. 基于TCP+TLS的低效传输

在web的世界里,我们以一次简单的https请求如以www.XXX.com为例,从发出请求到收到数据,我们可以很容易的从tcpdump和wireshark中发现整个链路中数据的通讯过程以及在这个过程中数据经历了一个什么样的流动,同时我们也可以很清楚的看到在真正用户数据之前需要做哪些前期准备。

第一,域名解析,域名解析一般由你使用网络运营商的DNS服务提供,运营商的DNS服务一般都有缓存,这个缓存会缩短网络时间。

其次,需要经历TCP建立连接的三次握手,这需要消耗1个RTT(最后一个ACK可以随数据一起发送);

接下来是TLS的握手操作,就以当前使用最为广泛的TLS1.2来说,四次TLS握手需要消耗完整的2个RTT;

前面做了这么多铺垫,最后才是真正的用户数据传输,假设用户的数据量很小,一个数据包就传输完了(比如1K左右),只需要一个1RTT。那么这里的数据传输效率就显得很低了,整个传输过程耗费了DNS时间+4个RTT的通讯时间,其中只有一个RTT是在承载用户数据,传输效率只有25%。可以从下面的数据传输序列图中清晰的看到每个阶段的通讯交互情况。

2. QUIC的性能提升

从上面的分析中可以看到,基于TCP的HTTPS传输所带来的额外开销变得很大,如何降低这75%的额外开销就变成了人们在网络传输优化路上一直追求的目标了。去掉TLS则安全性完全无法得到保障,明文传输,中间人攻击等等对数据在互联网上传输带来的巨大安全威胁。

传统的TCP性能提升做法

在安全日益成为人们生活必须部分的过程中,TLS现在只会增强,而不可能再去掉了。那么该如何提升数据的传输效率呢,在TLS层面后来出现了一些优化手段,例如TLS的握手链接复用等。使用sessionID或者ticket的方式来减少TLS握手的数据交互次数,使原本需要两个RTT的TLS握手在一部分情况下(产生连接复用的情况,例如曾经成功握手并且在有效期内)变成一个RTT,节省出来一个RTT使得效率可以从25%提升到33%。不仅仅是TLS层,在TCP上像google这类全球顶级的科技公司也一直致力于优化TCP的性能。其中有提出像TFO(tcp fastopen)这样的新特性用来提升tcp的传输效率,但是这些都有较高内核版本的要求,然而在全球来说整体网络TCP层的基础设施基本已经僵化,导致补丁升级或者新方案融入变得非常困难。

QUIC使用UDP的1-RTT握手效率提升

人们追求性能极致的脚步是无法阻挡的,在TCP基本僵化的情况下,google另辟蹊径,率先实验采用UDP来重新改写整个传输方案,逐渐形成了一套新的传输方案gQUIC。随后google向IETF提交实验性传输层网络协议QUIC的提案,在2016年11月国际互联网工程任务组(IETF)召开的第一次QUIC工作组会议,受到了业界的广泛关注。这也意味着QUIC开始了它的标准化过程,成为新一代传输层协议,形成了最新的iQUIC。在原始的gQUIC中是google自己设计的一套类似TLS的传输加密协议,然后当QUIC进入IETF后,随着整个QUIC的标准化进程,加上近年来出现TLS1.3版本,在性能和安全性上都有很大的提升,IETF在QUIC的加密协议上就放弃了google的加密协议使用了标准的TLS1.3。

QUIC的通讯过程在初次没有建立过连接时使用1-RTT的握手机制,同时保证连接的建立和达到安全的保障。以下是QUIC的1-RTT的握手过程:

  1. Server端会持有0-RTT公私钥对,并且生成SCFG(服务端的配置信息对象),把公钥放入SCFG中;

  2. 客户端初次请求时,需要向服务端获取0-RTT公钥,这个需要消耗一个RTT,这也QUIC的1-RTT的所在;

  3. 客户端在收到0-RTT公钥以后会缓存起来,同时生成自己的临时公私钥对,经过前面的一个RTT后客户端把自己的临时私钥与服务端发过来的0-RTT的公钥根据DH算法生成一个加密密钥K1,同时使用K1加密数据同时附送自己的临时公钥一起发送服务端,此时已有用户数据发送;

  4. 在服务端收到用户使用K1加密的用户数据和客户端发来的临时公钥以后,会做如下几件事:

  • 使用0-RTT私钥与客户端发来的临时公钥通过DH算法生成K1解密用户数据并递交到应用;

  • 生成服务端临时公私钥对,使用临时公私钥对的私钥,与客户端发来的客户端临时公钥,生成K2加密服务端要传输的数据

  • 把服务端的临时公钥和使用K2加密的应用数据发送到客户端

  1. 客户端收到服务端发送的服务端临时公钥和使用K2加密的应用数据后会再次使用DH算法把服务端的临时公钥和客户端原来的临时私钥重新生成K2解密数据,并且从此以后使用K2进行数据层的加解密

备注:

这里服务端为什么要重新再生成临时公私钥对再使用DH算法来生成加密密钥K2呢?其核心考虑到的是安全性,如果没有服务端的临时公私钥和K2,那么在通讯过程中使用的K1是不安全的,因为服务端的SCFG中的0-RTT公私钥是对所有客户端,并且长期保持直到过期,而且这个过期时间一般会比较长。一旦服务端的0-RTT私钥泄露则所有客户端的通讯都无法确保前向安全性了。攻击者只需要把包抓下来,获取到0-RTT私钥即可破解所有通讯数据。

QUIC的0-RTT握手效率极大提升

0-RTT是QUIC一个很关键的属性,能够在连接的第一个数据报文就可以携带用户数据。但是我们也可以看到如果客户端和服务端从来没有通讯过,那么是不存在0-RTT的,需要一个完成的RTT之后才能承载用户数据。

这个是QUIC的1-RTT过程,那么他的0-RTT又是怎么做的呢?其实很明显,客户端把0-RTT的握手公钥和相关信息保存起来,后续再建连接的时候就可以直接使用之前保存的数据了,只要这个数据没有过期,服务端都会承认的。因此可以避免掉公钥发送的这一个RTT,直接生成K1加密用户数据传输。

这个流程是gQUIC的流程,iQUIC由于使用的是TLS1.3,握手阶段报文的细节会有些不一样,例如首个请求的是证书、PSK等信息。在0-RTT阶段使用的是session复用的ticket方式。

从上面的分析我们可以看到QUIC在握手阶段的性能提升是很大的,最大延迟只增加一个RTT,性能上可以保持和基于明文的http一致,但是安全性可以和https保持一致。如果使用了0-RTT特性,将会更高效的提升数据效率,但是在安全性上会略有下降,因为0-RTT特性必然存在重放攻击。总体来说用户数据的传输效率有不小的提升,从原来最快的33%提升到了50%,0-RTT情况下甚至提升到了100%,从第一个报文就开始传输用户数据。

不管是gQUIC还是iQUIC,都把连接的管理和安全性合二为一融为一体,让传输协议具有原生安全属性。

3. QUIC的安全性分析

QUIC带来如此大的性能提升,是否就标志我们可以不顾一切,奋不顾身的把所有流量全部切入到QUIC呢?接下来我们一起看看QUIC的这些新的特性在安全性上如何,安全人员以及QUIC的使用人员需要注意哪些安全问题,以及QUIC在当前阶段在哪些类型的业务上使用是收效最大,哪些业务不适合QUIC或者不适合QUIC的某些特性。

协议不成熟性与产品稳定性带来的问题

QUIC作为一个新生代的协议,包括是拥塞控制算法也好,其他安全策略也好,成熟度都还不是特别高,当前在生产环境也没有一个通用性和兼容性成熟的实现,各个厂家都是根据自己的情况来实现QUIC部分的特性,还有部分特性并没有完全实现,或是实现的机制有待商榷,亦或是导致某些安全特性牺牲掉了,因此在使用过程中都会遇到各种各样的问题。在这种情况下,作为中小厂商使用一些开源实现的QUIC服务会带来很多问题,诸如安全性、稳定性、可靠性、资源消耗等等都会遇到很多挑战。完全自研实现却又需要耗费大量的人力物力,所以其实现在不太建议中小型厂商在QUIC上完全跟进。还是应该等待协议成熟,有成熟的产品以后再切入。

SCFG的签名计算安全问题

从前面的分析,我们可以看到SCFG的重要性非常关键,在0-RTT的场景完全依靠这个数据来获得0-RTT握手公钥,而且需要在客户端和服务端传输流转。那么它的安全可信就非常重要,QUIC是如何保障呢,如何防止中间人攻击呢,是否会带来其他安全风险?

在QUIC中给这个数据增加了一个签名机制,同时也设置了过期时间来保障安全性。签名是通过公有证书的私钥来签的,在客户端需要对证书进行认证,这样可以确保无法实现中间人攻击,SCFG的过期时间也可以很大程度上缓解SCFG被恶意收集。我们都知道签名是使用非对称算法来做的,如果使用RSA做签名则会使服务端的签名耗费大量的计算资源,借此攻击者可能会对服务端产生算力攻击导致DoS,在生产环境中需要使用硬件加速卡来offload签名计算,这样可以对其有效进行缓解,同时使用ECC的证书做签名也会比较好的计算性能。

0-RTT公钥泄露带来的安全问题

0-RTT公钥在客户端可以保存,在服务端也几乎能持久保持且各个客户端是共享的,同时在0-RTT的通讯过程中,0-RTT的第一个数据包就会携带用户数据(使用上述性能分析中的K1加密用户数据),因此带来的安全问题很明显,失去了前向安全性。因为数据包被抓下来之后一旦服务端0-RTT私有被泄漏则数据就可以被破解。

0-RTT前向安全性的破坏可以在下图中看到具体的过程

0-RTT造成重放攻击安全问题

任何0-RTT机制都无法避免重放攻击(不伦是TLS1.3的0-RTT,还是gQUIC的0-RTT机制),所有0-RTT机制在提升性能的同时也是对安全性有一个很大损伤的特性。

0-RTT不提供前向安全能力(PFS)

0-RTT首包没有源地址验证能力

0-RTT之后或者握手之后QUIC/TLS1.3提供了key exchange机制保障了PFS

前面的分析我们也看到了,0-RTT是没有前向安全能力的,数据可以持续被抓包,等待握手私钥泄露就可以破解数据。QUIC的非0-RTT数据包都有提供源地址的验证能力,在有风险的场景下可以发起源地址挑战验证。QUIC提供了一个STK机制,在客户端第一次发送数据包时服务端会根据数据包的源地址和服务器的时间戳等因子生成一个源地址TOKEN(STK),随后和响应数据包一起发送到客户端,而在后续的数据传输过程中客户端需要透传这个STK到服务端,从而服务端可以进行校验。当然这里服务端会为了性能考虑并非每次都会校验,而是在发现源地址和连接ID的对应关系发生变化或者连接迁移时会发起验证挑战。然而在0-RTT的首个数据包上无法进行此次验证。

对于安全性要求比较高的业务操作,例如具备有POST或者PUT操作时为了确保安全性通常会把0-RTT关闭,包括像facebook或者cloudflare也都是在一些关键操作上禁用0-RTT功能,只有幂等操作(如GET、HEAD等)才使用0-RTT。

重放攻击的过程:

UDP相比TCP的弱安全性问题

UDP的安全性存在的几个关键的地方,源地址欺骗攻击,UDP放大攻击等。在QUIC中有设计了源地址TOKEN(STK)验证的安全机制来解决源地址的欺骗攻击,在通讯过程中服务器要求确认客户端的源地址TOKEN,这个源地址TOKEN根据数据包的源地址和服务器的时间戳等因子生成STK,随后和响应数据包一起发送到客户端,而在后续的数据传输过程中客户端需要透传这个STK到服务端,从而服务端可以进行校验。当服务端发现连接对应的源地址发送变化时会主动发送RETRY报文进行服务端主动源地址验证。客户端也可以主动发起源地址验证信息。源地址验证可以保护两类攻击问题,源地址欺骗攻击和UDP放大攻击。

  • 连接建立时,为了验证客户端的地址是否是攻击者伪造的,服务端会生成一个令牌(token)并通过重试包(Retry packet)响应给客户端。客户端需要在后续的初始包(Initial packet)带上这个令牌,以便服务端进行地址验证。

  • 服务端可以在当前连接中通过 NEW_TOKEN 帧预先发布令牌,以便客户端在后续的新连接使用,这是 QUIC 实现 0-RTT 很重要的一个功能。

  • 当我们的网络路径变化时(比如从蜂窝网络切换到 WIFI),QUIC 提供了连接迁移(connection migration)的功能来避免连接中断。QUIC 通过路径验证(Path Validation)验证网络新地址的可达性(reachability),防止在连接迁移中的地址是攻击者伪造的。

由于握手的不对称性,还可以造成放大攻击:

QUIC协议的制定对放大攻击提供了一些缓解的方式如下:

  • 由于在0-RTT阶段缺失源地址验证能力,但要求完全填充数据包大小,使得放大系数大于<1;

  • 提供retry机制做源地址挑战验证;

  • 非0-RTT包源地址验证STK,STK的过期机制

  • HTTP / 3具有速率限制功能和短暂的验证令牌,可以充当DDOS攻击的补偿控制,同时部分缓解攻击情形

连接迁移造成的源地址欺骗和路径欺骗的安全问题

当我们的网络路径变化时(比如从蜂窝网络切换到WIFI或者NAT后的地址重绑定),QUIC 提供了连接迁移的功能来避免连接中断。

QUIC当源地址发生改变后并不马上终止传输进行地址验证挑战,而是基于性能等原因的考虑继续当前传输,在随后再进行源地址验证挑战或者路径验证挑战。那么在这中间会形成一个空档期可以进行地址欺骗攻击甚至是放大攻击。一些能够缓解的方式主要是在地址变动后可以做限速配置,直到完成新的挑战成功或者挑战失败退回到上一个连接。同时在验证失败后需要恢复到旧的有效连接以防止重置链路攻击。

当前的特性场景需要注意的点

由于当前QUIC协议的成熟性和安全性都存在有不足,所以在当前阶段有很多需要注意的点,以下做了一个简单的分析。

数据修改等场景需要限制使用

由0-RTT导致的前向安全性缺失和可重放攻击的问题可以看出,在很关键的场景例如转账操作等是不是适合开启0-RTT特性的,因为重放攻击会带来非常大的数据被篡改的问题。在业界很多顶级公司对安全非常关注,一般只会在幂等操作如GET操作等场景才开启0-RTT,其他场景会禁用0-RTT功能,甚至连接迁移的功能也是有限使用

有高安全场景需求的业务需要谨慎使用

对应安全性高的业务场景例如账号等在使用QUIC的时候当前也是持谨慎态度,握手阶段或者连接迁移阶段容易发生源地址欺骗可能会造成账号异常

视频、游戏等高实时要求场景的效果明显

由于QUIC在首次连接和网络类型切换过程中有很好的性能提升,对于下载类业务、视频类业务。尤其是在秒开场景会有非常好的提升效果。这类业务当下比较好的方式是信令数据走单独从传输方式(例如TCP等),而视频数据类可以走QUIC,可以更好的兼顾安全性与传输效率。

4. QUIC在OPPO的实践

OPPO在海外的业务发展非常迅速,用户量成指数级增长,已经达到亿级规模。在海外尤其是印度、印尼等东南亚地区网络的覆盖情况比较差,使用https链接的成功率比较低,下载延迟也比较大。同时用户在WiFi和4G的切换过程也非常多不稳定的情况,有些地方甚至出现QoS限流的情况。如何提升网络体验已经成为我们面临的非常大的难题。

安全增强的架构

为了更好的提升用户体验,解决这些问题,以及OPPO在技术上的极致追求,非常积极的跟进业绩先进技术的发展,在一些用户高体验场景下采用QUIC协议来提升用户体验。同时OPPO是一家非常关注安全的企业,在使用QUIC之前做了大量的调研工作,也对QUIC带来的网络性能提升以及带来的安全问题做了上面细致的分析。

下面我们列举了几个OPPO在安全上重点考虑的几个关键的点:

  • 在QUIC协议的基础增加WAF的支持;

  • 对于0-RTT特性谨慎使用;

  • 安全治理模块来对接QUIC的地址验证;

为了提升QUIC的安全能力,我们在使用QUIC基础支持WAF安全能力,把WAF直接融入到安全QUIC服务的处理中。同时从QUIC里面抽象出安全治理模块可以在很多场景直接发起对源地址的验证挑战。

在0-RTT的使用上,我们采用只有在幂等操作(如GET、HEAD等)的场景下才允许使用O-RTT,0-RTT作为一个非常谨慎使用的模块,在前期甚至都不支持这个特性。

性能实验结果

在OPPO弱网实验室的测试效果如下:

从实验室数据我们可以看到有非常好的提升,然而线上环境多变且复杂,在线上我们采取了谨慎的策略,目前部分海外业务已经陆续灰度上线。而从上线的效果来看,在弱网环境下对延迟的提升还是有比较好的效果,对于延迟大约有11%的提升,目前还只有部分灰度,线上环境相对比较复杂,可能有的用户本地网络对UDP的支持并不太好,在线上环境的拥塞控制也需要根据现实情况来单独优化。接下来还有许多功能特性需要持续支持,包括线上环境的拥塞控制,UDP包的底层处理通过DPDK来提升处理性能,提高系统利用率和吞吐。QUIC的安全治理模块目前还是实验阶段,后续还需要持续优化和调整,以达到更好的安全效果,平衡安全与性能。

IETF指定了QUIC作为HTTP3的传输承载协议,征求意见文档(RFC)有望在2021年会发出。届时会有更多的网站和应用程序会跑在QUIC上,同时业界头部厂商(google,facebook,腾讯,阿里,华为等)已逐步开启对QUIC的支持,相信其安全能力也会在随后的大规模运行中更加完善。对应我们而言,QUIC是一个起点,我们会更好的提升人们在OPPO上的用户体验。