作者:Jerry Qu
原文:https://imququ.com/post/optimize-tls-handshake.html

隨着 HTTP/2 的逐漸普及,以及國內網絡環境越來越糟糕(運營商劫持和篡改),HTTPS 已經開始成爲主流。HTTPS 在 TCP 和 HTTP 之間增加了 TLS(Transport Layer Security,傳輸層安全),提供了內容加密、身份認證和數據完整性三大功能,同時也給 Web 性能優化帶來新的挑戰。上次寫的「使用 BoringSSL 優化 HTTPS 加密算法選擇(https://imququ.com/post/optimize-ssl-ciphers-with-boringssl.html)」一文中,我介紹瞭如何針對不同平臺啓用最合適的傳輸加密算法。本篇文章我打算繼續寫 HTTPS 優化 —— TLS 握手優化。

TLS 的前身是 SSL(Secure Sockets Layer,安全套接字層),由網景公司開發,後來被 IETF 標準化並改名。通常沒有特別說明時,SSL 和 TLS 指的是同一個協議,不做嚴格區分。

TLS 握手

在傳輸應用數據之前,客戶端必須與服務端協商密鑰、加密算法等信息,服務端還要把自己的證書發給客戶端表明其身份,這些環節構成 TLS 握手過程,如下圖所示:


90%的人都不懂的TLS握手優化


可以看到,假設服務端和客戶端之間單次傳輸耗時 28ms,那麼客戶端需要等到 168ms 之後才能開始發送 HTTP 請求報文,這還沒把客戶端和服務端處理時間算進去。光是 TLS 握手就需要消耗兩個 RTT(Round-Trip Time,往返時間),這就是造成 HTTPS 更慢的主要原因。當然,HTTPS 要求數據加密傳輸,加解密相比 HTTP 也會帶來額外的開銷,不過對稱加密本來就很快,加上硬件性能越來越好,所以這部分開銷還好。

詳細的 TLS 握手過程這裏就不介紹了,大家可以通過這兩篇文章去了解:

  • 大型網站的 HTTPS 實踐(一)—— HTTPS 協議和原理(https://developer.baidu.com/resources/online/doc/security/https-pratice-1.html)
  • Keyless SSL: The Nitty Gritty Technical Details(https://blog.cloudflare.com/keyless-ssl-the-nitty-gritty-technical-details/)

通過 Wireshark 抓包可以清楚地看到完整 TLS 握手過程所需的兩個 RTT,如下圖:


90%的人都不懂的TLS握手優化


False Start

False Start 有搶跑的意思,意味着不按規則行事。TLS False Start 是指客戶端在發送 Change Cipher Spec Finished 同時發送應用數據(如 HTTP 請求),服務端在 TLS 握手完成時直接返回應用數據(如 HTTP 響應)。這樣,應用數據的發送實際上並未等到握手全部完成,故謂之搶跑。這個過程如下圖所示:


90%的人都不懂的TLS握手優化


可以看到,啓用 False Start 之後,TLS 階段只需要一次 RTT 就可以開始傳輸應用數據。False Start 相當於客戶端提前發送加密後的應用數據,不需要修改 TLS 協議,目前大部分瀏覽器默認都會啓用,但也有一些前提條件:

  • 服務端必須在 Server Hello 握手中通過 NPN(Next Protocol Negotiation,下一代協議協商,Google 在 SPDY 協議中開發的 TLS 擴展,用於握手階段協商應用協議)或 ALPN(Application Layer Protocol Negotiation,應用層協議協商,NPN 的官方修訂版)表明自己支持的 HTTP 協議,例如:http/1.1、http/2;
  • 使用支持前向安全性(Forward Secrecy)的加密算法。False Start 在尚未完成握手時就發送了應用數據,Forward Secrecy 可以提高安全性;

通過 Wireshark 抓包可以清楚地看到 False Start 帶來的好處(服務端的 ChangeCipherSpec 出現在 158 號包中,但在之前的 155 號包中,客戶端已經發出了請求,相當於 TLS 握手只消耗了一個 RTT):


90%的人都不懂的TLS握手優化


Certificate

TLS 的身份認證是通過證書信任鏈完成的,瀏覽器從站點證書開始遞歸校驗父證書,直至出現信任的根證書(根證書列表一般內置於操作系統,Firefox 自己維護)。站點證書是在 TLS 握手階段,由服務端發送的。

Certificate-Chain

配置服務端證書鏈時,有兩點需要注意:1)證書是在握手期間發送的,由於 TCP 初始擁塞窗口的存在,如果證書太長可能會產生額外的往返開銷;2)如果證書沒包含中間證書,大部分瀏覽器可以正常工作,但會暫停驗證並根據子證書指定的父證書 URL 自己獲取中間證書。這個過程會產生額外的 DNS 解析、建立 TCP 連接等開銷,非常影響性能。

配置證書鏈的最佳實踐是隻包含站點證書和中間證書,不要包含根證書,也不要漏掉中間證書。大部分證書鏈都是「站點證書 - 中間證書 - 根證書」這樣三級,這時服務端只需要發送前兩個證書即可。但也有的證書鏈有四級,那就需要發送站點證書外加兩個中間證書了。

通過 Wireshark 可以查看服務端發送的證書鏈情況,如下圖。可以看到本站發送了兩個證書,共 2270 字節,被分成 2 個 TCP 段來傳輸。這已經算小的了,理想的證書鏈應該控制在 3kb 以內。


90%的人都不懂的TLS握手優化


ECC Certificate

如果需要進一步減小證書大小,可以選擇 ECC(Elliptic Curve Cryptography,橢圓曲線密碼學)證書。256 位的 ECC Key 等同於 3072 位的 RSA Key,在確保安全性的同時,體積大幅減小。下面是一個對比:

90%的人都不懂的TLS握手優化

如果證書提供商支持 ECC 證書,使用以下命令生成 CSR(Certificate Signing Request,證書籤名請求)文件並提交給提供商,就可以獲得 ECC 證書:

openssl ecparam -genkey -name secp256r1 | openssl ec -out ecc.key
openssl req -new -key ecc.key -out ecc.csr

以上命令中可以選擇的算法有 secp256r1 和 secp384r1,secp521r1 已被 Chrome 和 Firefox 拋棄。

ECC 證書這麼好,爲什麼沒有普及呢?最主要的原因是它的支持情況並不好。例如 Windows XP 不支持,導致使用 ECC 證書的網站在 Windows XP 上只有 Firefox 能訪問(Firefox 證書那一套完全自己實現,不依賴操作系統)。另外,Android 平臺也只有 Android 4+ 才支持 ECC 證書。所以,確定使用 ECC 證書前需要明確用戶系統分佈情況。

Session Resumption

另外一個提高 TLS 握手效率的機制是會話複用。會話複用的原理很簡單,將第一次握手辛辛苦苦算出來的對稱密鑰存起來,後續請求中直接使用。這樣可以節省證書傳送等開銷,也可以將 TLS 握手所需 RTT 減少到一個,如下圖所示:


90%的人都不懂的TLS握手優化


可以看到會話複用機制生效時,雙方幾乎不怎麼交換數據就協商好密鑰了,這是怎麼做到的呢?

Session Identifier

Session Identifier(會話標識符),是 TLS 握手中生成的 Session ID。服務端可以將 Session ID 協商後的信息存起來,瀏覽器也可以保存 Session ID,並在後續的 ClientHello 握手中帶上它,如果服務端能找到與之匹配的信息,就可以完成一次快速握手。

Session Ticket

Session Identifier 機制有一些弊端,例如:1)負載均衡中,多機之間往往沒有同步 Session 信息,如果客戶端兩次請求沒有落在同一臺機器上就無法找到匹配的信息;2)服務端存儲 Session ID 對應的信息不好控制失效時間,太短起不到作用,太長又佔用服務端大量資源。

而 Session Ticket(會話記錄單)可以解決這些問題,Session Ticket 是用只有服務端知道的安全密鑰加密過的會話信息,最終保存在瀏覽器端。瀏覽器如果在 ClientHello 時帶上了 Session Ticket,只要服務器能成功解密就可以完成快速握手。

配置 Session Ticket 策略後,通過 Wireshark 可以看到服務端發送 Ticket 的過程:


90%的人都不懂的TLS握手優化


以下是 Session Resumption 機制生效時的握手情況,可以看到沒有發送證書等環節:


90%的人都不懂的TLS握手優化


測試

Github 上有一個名爲 rfc5077 的項目,非常適合用來測試服務端對 Session ID 和 Session Ticket 這兩種 TLS 會話複用機制的支持情況。下面簡單介紹如何使用這個工具。

首先,安裝工具所需依賴(以下所有步驟僅在 Ubuntu 14.04.4 LTS 測試通過):

sudo apt-get install -y pkg-config libssl-dev libev-dev libpcap-dev libgnutls-dev libnss3-dev

然後就可以獲取源碼,開始編譯:

git clone https://github.com/vincentbernat/rfc5077.gitcd rfc5077/
git submodule init
git submodule update
make

編譯完成後,當前目錄會出現多個可執行文件。這裏我們只會用到 rfc5077-client,它的用法是這樣的:

./rfc5077-client [-p {port}] [-s {sni name}] [-4] host [host ...]
  • -p 用於指定連接的端口,默認是 443;
  • -s 用於指定 SNI,如果同 IP 同端口部署了多個 HTTPS 網站,需要通過這個參數指定 SNI;
  • -4 表示只使用 IPV4 地址;
  • 命令最後需要可以跟一個或多個 HOST(域名或 IP);

這個工具會先禁用 Session Ticket 將所有 HOST 都測試一遍,然後開啓 Session Ticket 再測試一遍。下面是對本站兩個 IP 進行測試的命令:

./rfc5077-client -s imququ.com 114.215.116.12 139.162.98.188

測試結果如下(去掉了部分不重要的信息):

90%的人都不懂的TLS握手優化

90%的人都不懂的TLS握手優化

從以上結果可以看出:禁用 Session Ticket 時,每次連接到不同 IP 都會導致 Session 無法複用;而啓用 Session Ticket 後,不同 IP 之間也可以複用 Session。符合前面的結論。

值得注意的是,爲了讓一臺服務器生成的 Session Ticket 能被另外服務器承認,往往需要對 Web Server 進行額外配置。例如在 Nginx 中,就需要通過 ssl_session_ticket_key 參數讓多臺機器使用相同的 key 文件,否則 Nginx 會使用隨機生成的 key 文件,無法複用 Session Ticket。出於安全考慮,key 文件應該定期更換,並且確保換下來的 key 文件被徹底銷燬。

OCSP Stapling

出於某些原因,證書頒發者有時候需要作廢某些證書。那麼證書使用者(例如瀏覽器)如何知道一個證書是否已被作廢呢?通常有兩種方式:CRL(Certificate Revocation List,證書撤銷名單)和 OCSP(Online Certificate Status Protocol,在線證書狀態協議)。

CRL 是由證書頒發機構定期更新的一個列表,包含了所有已被作廢的證書,瀏覽器可以定期下載這個列表用於驗證證書合法性。不難想象,CRL 會隨着時間推移變得越來越大,而且實時性很難得到保證。OCSP 是一個在線查詢接口,瀏覽器可以實時查詢單個證書的合法性。在每個證書的詳細信息中,都可以找到對應頒發機構的 CRL 和 OCSP 地址。

OCSP 的問題在於,某些客戶端會在 TLS 握手階段進一步協商時,實時查詢 OCSP 接口,並在獲得結果前阻塞後續流程,這對性能影響很大。而 OCSP Stapling(OCSP 封套),是指服務端在證書鏈中包含頒發機構對證書的 OCSP 查詢結果,從而讓瀏覽器跳過自己去驗證的過程。服務端有更快的網絡,獲取 OCSP 響應更容易,也可以將 OCSP 響應緩存起來。

OCSP 響應本身經過了數字簽名,無法僞造,所以 OCSP Stapling 技術既提高了握手效率,也不會影響安全性。啓用這項技術後,也可以通過 Wireshark 來驗證:


90%的人都不懂的TLS握手優化


可以看到,服務端在發送完證書後,緊接着又發來了它的 OCSP 響應,從而避免了瀏覽器自己去驗證證書造成阻塞。需要注意的是,OCSP Stapling 只能包含一個 OCSP 響應,瀏覽器還是可能自己去驗證中間證書。另外,OCSP 響應本身會佔用幾 kb 的大小。

OCSP Stapling 功能需要 Web Server 的支持,主流的 Nginx、Apache 和 H2O 都支持 —— 但同時還取決於使用的 SSL 庫 —— 例如 BoringSSL 不支持 OCSP Stapling,使用 BoringSSL + Nginx 就無法開啓 OCSP Stapling。

如何使用 Nginx 配置本文這些策略,可以參考我之前的文章:本博客 Nginx 配置之性能篇。

最後,強烈推薦 Qualys SSL Labs 的 SSL Server Test 工具,可以幫你查出 HTTPS 很多配置上的問題。本博客的測試結果見這裏。

本文一部分內容來自於 Google 性能專家 Ilya Grigorik 寫的《High Performance Browser Networking》第四章:Transport Layer Security (TLS)。這是一本可以免費在線閱讀,一直都在更新的性能優化好書,本博客多次推薦。本書中文翻譯由李鬆峯老師負責,已經出版,名爲《WEB 性能權威指南》。

相關文章