今天來(lái)給大家談?wù)凥TTPS 的 7 次握手以及 9 倍時(shí)延。
HTTP 協(xié)議(Hypertext Transfer Protocol)已經(jīng)成為互聯(lián)網(wǎng)上最常用的應(yīng)用層協(xié)議,然而其本身只是用于傳輸超文本的網(wǎng)絡(luò)協(xié)議,不會(huì)提供任何安全上的保證,使用明文在互聯(lián)網(wǎng)上傳輸數(shù)據(jù)包使得竊聽(tīng)和中間人攻擊成為可能,通過(guò) HTTP 傳輸密碼其實(shí)與在互聯(lián)網(wǎng)上裸奔也差不多。

網(wǎng)景(Netscape)在 1994 年設(shè)計(jì)了 HTTPS 協(xié)議,使用安全套接字層(Secure Sockets Layer,SSL)保證數(shù)據(jù)傳輸?shù)陌踩玔^1],隨著傳輸層安全協(xié)議(Transport Layer Security,TLS)的發(fā)展,目前我們已經(jīng)使用 TLS 取代了廢棄的 SSL 協(xié)議,不過(guò)仍然使用 SSL 證書(shū)一詞[^2]。
HTTPS 是對(duì) HTTP 協(xié)議的擴(kuò)展,我們可以使用它在互聯(lián)網(wǎng)上安全地傳輸數(shù)據(jù)[^3],然而 HTTPS 請(qǐng)求的發(fā)起方第一次從接收方獲取響應(yīng)需要經(jīng)過(guò) 4.5 倍的往返延遲(Round-Trip Time,RTT)。本文將詳細(xì)介紹請(qǐng)求發(fā)起和響應(yīng)的過(guò)程,分析為什么 HTTPS 協(xié)議需要通過(guò) 4.5-RTT 的時(shí)間獲得服務(wù)提供方的響應(yīng):
- TCP 協(xié)議 — 通信雙方通過(guò)三次握手建立 TCP 連接[^4];
- TLS 協(xié)議 — 通信雙方通過(guò)四次握手建立 TLS 連接[^5];
- HTTP 協(xié)議 — 客戶端向服務(wù)端發(fā)送請(qǐng)求,服務(wù)端發(fā)回響應(yīng);
這里的分析建立在特定版本的協(xié)議實(shí)現(xiàn)以及常見(jiàn)場(chǎng)景上,隨著網(wǎng)絡(luò)技術(shù)的發(fā)展,我們能夠減少需要的網(wǎng)絡(luò)通信次數(shù),本文會(huì)在對(duì)應(yīng)章節(jié)中提到一些常見(jiàn)的優(yōu)化方案。
TCP
HTTP 協(xié)議作為應(yīng)用層協(xié)議,它需要底層的傳輸層協(xié)議為其提供基本的數(shù)據(jù)傳輸功能,HTTP 協(xié)議一般都會(huì)使用 TCP 協(xié)議作為底層協(xié)議。為了阻止錯(cuò)誤的建立歷史連接,TCP 協(xié)議通信的雙方會(huì)通過(guò)三次握手建立 TCP 連接[^6],我們?cè)谶@里簡(jiǎn)單回顧一下 TCP 連接建立的整個(gè)過(guò)程。

- 客戶端向服務(wù)端發(fā)送帶有 SYN 的數(shù)據(jù)段以及客戶端開(kāi)始發(fā)送數(shù)據(jù)段(Segment)的初始序列號(hào) SEQ = 100;
- 服務(wù)端收到數(shù)據(jù)段時(shí),向客戶端發(fā)送帶有 SYN 和 ACK 的數(shù)據(jù)段;
- 通過(guò)返回 ACK = 101 確認(rèn)客戶端數(shù)據(jù)段的初始序列號(hào);
- 通過(guò)發(fā)送 SEQ = 300 通知客戶端,服務(wù)端開(kāi)始發(fā)送數(shù)據(jù)段的初始序列號(hào);
- 客戶端向服務(wù)端發(fā)送帶有 ACK 的數(shù)據(jù)段,確認(rèn)服務(wù)端的初始序列號(hào),其中包含 ACK = 301;
TCP 連接的雙方會(huì)通過(guò)三次握手確定 TCP 連接的初始序列號(hào)、窗口大小以及最大數(shù)據(jù)段,這樣通信雙方就能利用連接中的初始序列號(hào)保證雙方數(shù)據(jù)段的不重不漏、通過(guò)窗口大小控制流量并使用最大數(shù)據(jù)段避免 IP 協(xié)議對(duì)數(shù)據(jù)包的分片[^7]。
最初版本的 TCP 協(xié)議確實(shí)會(huì)通過(guò)三次通信建立 TCP 連接,在目前的大多數(shù)場(chǎng)景下,三次握手也是無(wú)法避免的,不過(guò)在 2014 年提出的 TCP 快啟(TCP Fast Open,TFO)卻可以在某些場(chǎng)景下通過(guò)一次通信建立 TCP 連接[^8]。
TCP 快啟策略使用存儲(chǔ)在客戶端的 TFO Cookie 與服務(wù)端快速建立連接。TCP 連接的客戶端向服務(wù)端發(fā)送 SYN 消息時(shí)會(huì)攜帶快啟選項(xiàng),服務(wù)端會(huì)生成一個(gè) Cookie 并將其發(fā)送至客戶端,客戶端會(huì)緩存該 Cookie,當(dāng)其與服務(wù)端重新建立連接時(shí),它會(huì)使用存儲(chǔ)的 Cookie 直接建立 TCP 連接,服務(wù)端驗(yàn)證 Cookie 后會(huì)向客戶端發(fā)送 SYN 和 ACK 并開(kāi)始傳輸數(shù)據(jù),這也就能減少通信的次數(shù)。
TLS
TLS 的作用是在可靠的 TCP 協(xié)議上構(gòu)建安全的傳輸通道,其本身是不提供可靠性保障的,我們還是需要下層可靠的傳輸層協(xié)議。在通信雙方建立可靠的 TCP 連接之后,我們就需要通過(guò) TLS 握手交換雙方的密鑰了,在這里我們將介紹 TLS 1.2 的連接建立過(guò)程[^9]:

- 客戶端向服務(wù)端發(fā)送 Client Hello 消息,其中攜帶客戶端支持的協(xié)議版本、加密算法、壓縮算法以及客戶端生成的隨機(jī)數(shù);
- 服務(wù)端收到客戶端支持的協(xié)議版本、加密算法等信息后;
- 向客戶端發(fā)送 Server Hello 消息,并攜帶選擇特定的協(xié)議版本、加密方法、會(huì)話 ID 以及服務(wù)端生成的隨機(jī)數(shù);
- 向客戶端發(fā)送 Certificate 消息,即服務(wù)端的證書(shū)鏈,其中包含證書(shū)支持的域名、發(fā)行方和有效期等信息;
- 向客戶端發(fā)送 Server Key Exchange 消息,傳遞公鑰以及簽名等信息;
- 向客戶端發(fā)送可選的消息 CertificateRequest,驗(yàn)證客戶端的證書(shū);
- 向客戶端發(fā)送 Server Hello Done 消息,通知服務(wù)端已經(jīng)發(fā)送了全部的相關(guān)信息;
- 客戶端收到服務(wù)端的協(xié)議版本、加密方法、會(huì)話 ID 以及證書(shū)等信息后,驗(yàn)證服務(wù)端的證書(shū);
- 向服務(wù)端發(fā)送 Client Key Exchange 消息,包含使用服務(wù)端公鑰加密后的隨機(jī)字符串,即預(yù)主密鑰(Pre Master Secret);
- 向服務(wù)端發(fā)送 Change Cipher Spec 消息,通知服務(wù)端后面的數(shù)據(jù)段會(huì)加密傳輸;
- 向服務(wù)端發(fā)送 Finished 消息,其中包含加密后的握手信息;
- 服務(wù)端收到 Change Cipher Spec 和 Finished 消息后;
- 向客戶端發(fā)送 Change Cipher Spec 消息,通知客戶端后面的數(shù)據(jù)段會(huì)加密傳輸;
- 向客戶端發(fā)送 Finished 消息,驗(yàn)證客戶端的 Finished 消息并完成 TLS 握手;
TLS 握手的關(guān)鍵在于利用通信雙方生成的隨機(jī)字符串和服務(wù)端的公鑰生成一個(gè)雙方經(jīng)過(guò)協(xié)商后的密鑰,通信的雙方可以使用這個(gè)對(duì)稱的密鑰加密消息防止中間人的監(jiān)聽(tīng)和攻擊,保證通信的安全。
在 TLS 1.2 中,我們需要 2-RTT 才能建立 TLS 連接[^10],但是 TLS 1.3 通過(guò)優(yōu)化協(xié)議,將兩次往返延遲降低至一次,大幅度減少建立 TLS 連接所需要的時(shí)間,讓客戶端可以在 1-RTT 之后就能向服務(wù)端傳輸應(yīng)用層數(shù)據(jù)。
這里就不展開(kāi)介紹 TLS 1.3 建立連接的過(guò)程了,除了減少常規(guī)握手下的網(wǎng)絡(luò)開(kāi)銷,TLS 1.3 還引入了 0-RTT 的連接建立過(guò)程;60% 的網(wǎng)絡(luò)連接都是用戶在第一次訪問(wèn)網(wǎng)站或者間隔一段時(shí)間后訪問(wèn)時(shí)建立的,剩下的 40% 可以通過(guò) TLS 1.3 的 0-RTT 策略解決[^11],然而該策略與 TFO 的實(shí)現(xiàn)原理比較相似,都是通過(guò)重用會(huì)話和緩存來(lái)實(shí)現(xiàn)的,所以存在一定的安全風(fēng)險(xiǎn),使用時(shí)也應(yīng)該結(jié)合業(yè)務(wù)的具體場(chǎng)景。
HTTP
在已經(jīng)建立好 TCP 和 TLS 通道上傳輸數(shù)據(jù)是比較簡(jiǎn)單的事情,HTTP 協(xié)議可以直接利用下層建立的可靠的、安全的通道傳輸數(shù)據(jù)。客戶端通過(guò) TCP 的套接字接口向服務(wù)端寫(xiě)入數(shù)據(jù),服務(wù)端在接收到數(shù)據(jù)、進(jìn)行處理后通過(guò)相同的途徑返回。因?yàn)檎麄€(gè)過(guò)程需要客戶端發(fā)送請(qǐng)求以及服務(wù)端返回響應(yīng),所以耗時(shí)是 1-RTT。

http-request-and-response
HTTP 協(xié)議的數(shù)據(jù)交換只會(huì)消耗 1-RTT,當(dāng)客戶端和服務(wù)端僅處理一次 HTTP 請(qǐng)求時(shí),從 HTTP 協(xié)議本身我們已經(jīng)無(wú)法進(jìn)行優(yōu)化。不過(guò)隨著請(qǐng)求的數(shù)量逐漸增加,HTTP/2 就可以復(fù)用已經(jīng)建立的 TCP 連接減少 TCP 和 TLS 握手帶來(lái)的額外開(kāi)銷。
總結(jié)
當(dāng)客戶端想要通過(guò) HTTPS 請(qǐng)求訪問(wèn)服務(wù)端時(shí),整個(gè)過(guò)程需要經(jīng)過(guò) 7 次握手并消耗 9 倍的延遲。如果客戶端和服務(wù)端因?yàn)槲锢砭嚯x上的限制,RTT 約為 40ms 時(shí),第一次請(qǐng)求需要 ~180ms;不過(guò)如果我們想要訪問(wèn)美國(guó)的服務(wù)器,RTT 約為 200ms 時(shí),這時(shí) HTTPS 請(qǐng)求的耗時(shí)為 ~900ms,這就是一個(gè)比較高的耗時(shí)了。我們來(lái)總結(jié)一下 HTTPS 協(xié)議需要 9 倍時(shí)延才能完成通信的原因:
- TCP 協(xié)議需要通過(guò)三次握手建立 TCP 連接保證通信的可靠性(1.5-RTT);
- TLS 協(xié)議會(huì)在 TCP 協(xié)議之上通過(guò)四次握手建立 TLS 連接保證通信的安全性(2-RTT);
- HTTP 協(xié)議會(huì)在 TCP 和 TLS 上通過(guò)一次往返發(fā)送請(qǐng)求并接收響應(yīng)(1-RTT);
需要注意的是,對(duì)往返延時(shí)的計(jì)算都基于特定的場(chǎng)景以及特定的協(xié)議版本,網(wǎng)絡(luò)協(xié)議的版本在不斷更新和演進(jìn),過(guò)去忽略的問(wèn)題最開(kāi)始都會(huì)通過(guò)補(bǔ)丁的方式更新,但是最后仍然會(huì)需要從底層完成重寫(xiě)。
HTTP/3 就是一個(gè)這樣的例子,它會(huì)使用基于 UDP 的 QUIC 協(xié)議進(jìn)行握手,將 TCP 和 TLS 的握手過(guò)程結(jié)合起來(lái),把 7 次握手減少到了 3 次握手,直接建立了可靠并且安全的傳輸通道,將原本 ~900ms 的耗時(shí)降低至 ~500ms,我們會(huì)在后面的文章介紹 HTTP/3 協(xié)議相關(guān)的內(nèi)容。到最后,我們還是來(lái)看一些比較開(kāi)放的相關(guān)問(wèn)題,有興趣的讀者可以仔細(xì)思考一下下面