這種公司不去也罷!
大家好,我是小林。上周有位讀者在面試的時候,碰到這么個問題:
面試官跟他說 HTTPS 中的 TLS 握手過程可以同時進行三次握手,然后讀者之前看我的文章是說「先進行 TCP 三次握手,再進行 TLS 四次握手」,他跟面試官說了這個,面試官說他不對,他就感到很困惑。
我們先不管面試官說的那句「HTTPS 中的 TLS 握手過程可以同時進行三次握手」對不對。
但是面試官說「HTTPS 建立連接的過程,先進行 TCP 三次握手,再進行 TLS 四次握手」是錯的,這很明顯面試官的水平有問題,這種公司不去也罷!
如果是我面試遇到這樣的面試官,我直接當場給他抓 HTTPS 建立過程的網絡包,然后給他看,啪啪啪啪啪的打他臉。
比如,下面這個 TLSv1.2 的 基于 RSA 算法的四次握手過程:
難道不是先三次握手,再進行 TLS 四次握手嗎?面試官你臉疼嗎?
不過 TLS 握手過程的次數還得看版本。
TLSv1.2 握手過程基本都是需要四次,也就是需要經過 2-RTT 才能完成握手,然后才能發送請求,而 TLSv1.3 只需要 1-RTT 就能完成 TLS 握手,如下圖。
一般情況下,不管 TLS 握手次數如何,都得先經過 TCP 三次握手后才能進行,因為 HTTPS 都是基于 TCP 傳輸協議實現的,得先建立完可靠的 TCP 連接才能做 TLS 握手的事情。
那面試官說的這句「HTTPS 中的 TLS 握手過程可以同時進行三次握手」對不對呢?
這個場景是可能發生的,但是需要在特定的條件下才可能發生,如果沒有說任何前提條件,說這句話就是在耍流氓。
那到底什么條件下,這個場景才能發生呢?需要下面這兩個條件同時滿足才可以:
- 客戶端和服務端都開啟了 TCP Fast Open 功能,且 TLS 版本是 1.3;
- 客戶端和服務端已經完成過一次通信。
那具體怎么做到的呢?我們先了解些 TCP Fast Open 功能和 TLSv1.3 的特性。
TCP Fast Open
我們先來了解下什么是 TCP Fast Open?
常規的情況下,如果要使用 TCP 傳輸協議進行通信,則客戶端和服務端通信之前,先要經過 TCP 三次握手后,建立完可靠的 TCP 連接后,客戶端才能將數據發送給服務端。
其中,TCP 的第一次和第二次握手是不能夠攜帶數據的,而 TCP 的第三次握手是可以攜帶數據的,因為這時候客戶端的 TCP 連接狀態已經是 ESTABLISHED,表明客戶端這一方已經完成了 TCP 連接建立。
就算客戶端攜帶數據的第三次握手在網絡中丟失了,客戶端在一定時間內沒有收到服務端對該數據的應答報文,就會觸發超時重傳機制,然后客戶端重傳該攜帶數據的第三次握手的報文,直到重傳次數達到系統的閾值,客戶端就會銷毀該 TCP 連接。
說完常規的 TCP 連接后,我們再來看看 TCP Fast Open。
TCP Fast Open 是為了繞過 TCP 三次握手發送數據,在 Linux 3.7 內核版本之后,提供了 TCP Fast Open 功能,這個功能可以減少 TCP 連接建立的時延。
要使用 TCP Fast Open 功能,客戶端和服務端都要同時支持才會生效。
不過,開啟了 TCP Fast Open 功能,想要繞過 TCP 三次握手發送數據,得建立第二次以后的通信過程。
在客戶端首次建立連接時的過程,如下圖:
具體介紹:
- 客戶端發送 SYN 報文,該報文包含 Fast Open 選項,且該選項的 Cookie 為空,這表明客戶端請求 Fast Open Cookie;
- 支持 TCP Fast Open 的服務器生成 Cookie,并將其置于 SYN-ACK 報文中的 Fast Open 選項以發回客戶端;
- 客戶端收到 SYN-ACK 后,本地緩存 Fast Open 選項中的 Cookie。
所以,第一次客戶端和服務端通信的時候,還是需要正常的三次握手流程。隨后,客戶端就有了 Cookie 這個東西,它可以用來向服務器 TCP 證明先前與客戶端 IP 地址的三向握手已成功完成。
對于客戶端與服務端的后續通信,客戶端可以在第一次握手的時候攜帶應用數據,從而達到繞過三次握手發送數據的效果,整個過程如下圖:
我詳細介紹下這個過程:
- 客戶端發送 SYN 報文,該報文可以攜帶「應用數據」以及此前記錄的 Cookie;
- 支持 TCP Fast Open 的服務器會對收到 Cookie 進行校驗:如果 Cookie 有效,服務器將在 SYN-ACK 報文中對 SYN 和「數據」進行確認,服務器隨后將「應用數據」遞送給對應的應用程序;如果 Cookie 無效,服務器將丟棄 SYN 報文中包含的「應用數據」,且其隨后發出的 SYN-ACK 報文將只確認 SYN 的對應序列號;
- 如果服務器接受了 SYN 報文中的「應用數據」,服務器可在握手完成之前發送「響應數據」,這就減少了握手帶來的 1 個 RTT 的時間消耗;
- 客戶端將發送 ACK 確認服務器發回的 SYN 以及「應用數據」,但如果客戶端在初始的 SYN 報文中發送的「應用數據」沒有被確認,則客戶端將重新發送「應用數據」;
- 此后的 TCP 連接的數據傳輸過程和非 TCP Fast Open 的正常情況一致。
所以,如果客戶端和服務端同時支持 TCP Fast Open 功能,那么在完成首次通信過程后,后續客戶端與服務端 的通信則可以繞過三次握手發送數據,這就減少了握手帶來的 1 個 RTT 的時間消耗。
TLSv1.3
說完 TCP Fast Open,再來看看 TLSv1.3。
在最開始的時候,我也提到 TLSv1.3 握手過程只需 1-RTT 的時間,它到整個握手過程,如下圖:
TCP 連接的第三次握手是可以攜帶數據的,如果客戶端在第三次握手發送了 TLSv1.3 第一次握手數據,是不是就表示「HTTPS 中的 TLS 握手過程可以同時進行三次握手」?。
不是的,因為服務端只有在收到客戶端的 TCP 的第三次握手后,才能和客戶端進行后續 TLSv1.3 握手。TLSv1.3 還有個更厲害到地方在于會話恢復機制,在重連 TLvS1.3 只需要 0-RTT,用“pre_shared_key”和“early_data”擴展,在 TCP 連接后立即就建立安全連接發送加密消息,過程如下圖:
TCP Fast Open + TLSv1.3
在前面我們知道,客戶端和服務端同時支持 TCP Fast Open 功能的情況下,在第二次以后到通信過程中,客戶端可以繞過三次握手直接發送數據,而且服務端也不需要等收到第三次握手后才發送數據。
如果 HTTPS 的 TLS 版本是 1.3,那么 TLS 過程只需要 1-RTT。
因此如果「TCP Fast Open + TLSv1.3」情況下,在第二次以后的通信過程中,TLS 和 TCP 的握手過程是可以同時進行的。
如果基于 TCP Fast Open 場景下的 TLSv1.3 0-RTT 會話恢復過程,不僅 TLS 和 TCP 的握手過程是可以同時進行的,而且 HTTP 請求也可以在這期間內一同完成。
總結
最后做個總結。
「HTTPS 是先進行 TCP 三次握手,再進行 TLSv1.2 四次握手」,這句話一點問題都沒有,懷疑這句話是錯的人,才有問題。
「HTTPS 中的 TLS 握手過程可以同時進行三次握手」,這個場景是可能存在到,但是在沒有說任何前提條件,而說這句話就等于耍流氓。需要下面這兩個條件同時滿足才可以:
- 客戶端和服務端都開啟了 TCP Fast Open 功能,且 TLS 版本是 1.3;
- 客戶端和服務端已經完成過一次通信;
怎么樣,那位“面試官”學廢了嗎?