Google、Facebook等均开始支持的HTTP3到底是个什么鬼?
共 6619字,需浏览 14分钟
·
2020-12-21 15:46
最近一段时间以来,关于HTTP/3的新闻有很多,越来越多的国际大公司已经开始使用HTTP/3了。
所以,HTTP/3已经是箭在弦上了,全面使用只是个时间问题,那么,作为一线开发者,我们也是时候了解下到底什么是HTTP/3,为什么需要HTTP/3了。
基于UDP的传输层协议:它使用UDP端口号来识别指定机器上的特定服务器。 可靠性:虽然UDP是不可靠传输协议,但是QUIC在UDP的基础上做了些改造,使得他提供了和TCP类似的可靠性。它提供了数据包重传、拥塞控制、调整传输节奏以及其他一些TCP中存在的特性。 实现了无序、并发字节流:QUIC的单个数据流可以保证有序交付,但多个数据流之间可能乱序,这意味着单个数据流的传输是按序的,但是多个数据流中接收方收到的顺序可能与发送方的发送顺序不同! 快速握手:QUIC提供0-RTT和1-RTT的连接建立 使用TLS 1.3传输层安全协议:与更早的TLS版本相比,TLS 1.3有着很多优点,但使用它的最主要原因是其握手所花费的往返次数更低,从而能降低协议的延迟。
如果更加细化一点的话,因为QUIC不仅仅承担了传输层协议的职责,还具备了TLS的安全性相关能力,所以,可以通过下图来理解QUIC在HTTP/3的实现中所处的位置。
接下来我们分别展开分析一下QUIC协议。先来看下他是如何建立连接的。
QUIC在握手过程中使用Diffie-Hellman算法来保证数据交互的安全性并合并了它的加密和握手过程来减小连接建立过程中的往返次数。
Diffie–Hellman (以下简称DH)密钥交换是一个特殊的交换密钥的方法。它是密码学领域内最早付诸实践的密钥交换方法之一。DH可以让双方在完全缺乏对方(私有)信息的前提条件下通过不安全的信道达成一个共享的密钥。此密钥用于对后续信息交换进行对称加密。
QUIC 连接的建立整体流程大致为:QUIC在握手过程中使用Diffie-Hellman算法协商初始密钥,初始密钥依赖于服务器存储的一组配置参数,该参数会周期性的更新。初始密钥协商成功后,服务器会提供一个临时随机数,双方根据这个数再生成会话密钥。客户端和服务器会使用新生的的密钥进行数据加解密。
初始握手(Initial handshake)
在连接开始建立时,客户端会向服务端发送一个打招呼信息,(inchoate client hello (CHLO)),因为是初次建立,所以,服务端会返回一个拒绝消息(REJ),表明握手未建立或者密钥已过期。
但是,这个拒绝消息中还会包含更多的信息(配置参数),主要有:
Server Config:一个服务器配置,包括服务器端的Diffie-Hellman算法的长期公钥(long term Diffie-Hellman public value) Certificate Chain:用来对服务器进行认证的信任链 Signature of the Server Config:将Server Config使用信任链的叶子证书的public key加密后的签名 Source-Address Token:一个经过身份验证的加密块,包含客户端公开可见的IP地址和服务器的时间戳。
之后,客户端会将自己刚刚产生的短期公钥打包一个Complete CHLO的消息包中,发送给服务端。这个请求的目的是将自己的短期密钥传输给服务端,方便做前向保密,后面篇幅会详细介绍。
在发送了Complete CHLO消息给到服务器之后,为了减少RTT,客户端并不会等到服务器的响应,而是立刻会进行数据传输。
有了这个初识密钥之后,客户端就可以用这个密钥,将想要传输的信息进行加密,然后把他们安全的传输给服务端了。
另外一面,接收到Complete CHLO请求的服务器,解析请求之后,就同时拥有了客户端的短期公钥和自己保存的长期密钥。这样通过运算,服务端就能得到一份和客户端一模一样的初始密钥(initial keys)。
所以,为了达到前向保密 (Forward Secrecy) 的安全性,客户端和服务端需要使用彼此的短期公钥和自己的短期密钥来进行运算。
在密码学中,前向保密(英语:Forward Secrecy,FS)是密码学中通讯协议的安全属性,指的是长期使用的主密钥泄漏不会导致过去的会话密钥泄漏。
那么现在问题是,客户端的短期密钥已经发送给服务端,而服务端只把自己的长期密钥给了客户端,并没有给到自己的短期密钥。
所以,服务端在收到Complete CHLO之后,会给到服务器一个server hello(SHLO)消息,这个消息会使用初始密钥(initial keys)进行加密。
这个CHLO消息包中,会包含一个服务端重新生成的短期公钥。
当下一次要重新创建连接的时候,客户端会从缓存中取出自己之前缓存下来的服务器的长期公钥,并重新创建一个短期密钥,重新生成一个初始密钥,再使用这个初始密钥对想要传输的数据进行加密,向服务器发送一个Complete CHLO 请求即可。这样就达到了0 RTT的数据传输。
所以,如果是有缓存的长期公钥,那么数据传输就会直接进行,准备时间是0 RTT
TCP传输过程中会把数据拆分为一个个按照顺序排列的数据包,这些数据包通过网络传输到了接收端,接收端再按照顺序将这些数据包组合成原始数据,这样就完成了数据传输。
但是如果其中的某一个数据包没有按照顺序到达,接收端会一直保持连接等待数据包返回,这时候就会阻塞后续请求。这就发生了TCP队头阻塞。
数据流(Streams)在QUIC中提供了一个轻量级、有序的字节流的抽象化
也就是说同一个连接上面的多个数据流之间没有任何依赖(不要求按照顺序到达),即使某一个数据包没有达到,也只会影响自己这个数据流,并不会影响到到其他的数据流。
特性 | HTTP/2 | HTTP/3 |
---|---|---|
传输层协议 | TCP | 基于UDP的QUIC |
默认加密 | 否 | 是 |
独立的数据流 | 否 | 是 |
队头阻塞 | 存在TCP队头阻塞 | 无 |
报头压缩 | HPACK | QPACK |
握手时延 | TCP+TLS 的 1-3 RTT | 0-1 RTT |
连接迁移 | 无 | 有 |
服务器推送 | 有 | 有 |
多路复用 | 有 | 有 |
流量控制 | 有 | 有 |
数据重传 | 有 | 有 |
拥塞控制 | 有 | 有 |
参考资料:
https://http3-explained.haxx.se/
The QUIC Transport Protocol: Design and Internet-Scale Deployment
https://www.codenong.com/cs106840038/
https://nan01ab.github.io/2018/12/QUIC.html
https://medium.com/@chester.yw.chu/http-3-%E5%82%B3%E8%BC%B8%E5%8D%94%E8%AD%B0-quic-%E7%B0%A1%E4%BB%8B-5f8806d6c8cd
往期推荐
HTTP/2做错了什么?刚刚辉煌2年就要被弃用了!?
央视曝光:全国第九大电商平台倒了!创始人卷走260亿,1200万人被骗
那个 CEO 写下 70 万行代码的公司,马上要上市了
直面Java第343期:为什么TOMCAT要破坏双亲委派
深入并发第013期:拓展synchronized——锁优化