SSL/TLS 握手優化詳解

發表於2015-11-10

隨著 HTTP/2 的逐漸普及,以及國內網路環境越來越糟糕(運營商劫持和篡改),HTTPS 已經開始成為主流。HTTPS 在 TCP 和 HTTP 之間增加了 TLS(Transport Layer Security),保證了傳輸層安全,同時也給 Web 效能優化帶來新的挑戰。上次寫的「使用 BoringSSL 優化 HTTPS 加密演算法選擇」一文中,我介紹瞭如何針對不同平臺啟用最合適的傳輸加密演算法。本篇文章我打算繼續寫 HTTPS 優化 —— TLS 握手優化。

先來補充一個小知識:SSL(Secure Sockets Layer,安全套接字層)最初是由網景公司開發的協議,提供了內容加密、身份認證和資料完整性三大功能。IETF 後來在標準化 SSL 協議時,將其改名為 TLS,主要功能沒有變化。通常沒有特別說明時,SSL 和 TLS 指的是同一個協議,本文也不做嚴格區分。

TLS 握手

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

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

詳細的 TLS 握手過程這裡就不介紹了,大家可以通過這篇《大型網站的 HTTPS 實踐(1):HTTPS 協議和原理》去了解。通過 Wireshark 抓包可以清楚地看到完整 TLS 握手過程所需的兩個 RTT,如下圖:

False Start

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

可以看到,啟用 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 帶來的好處(編號為 32 的包已經捎帶傳送了請求,並在 34 號包得到響應,相當於 TLS 握手只消耗了一個 RTT):

Certificate

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

Certificate-Chain

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

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

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

ECC Certificate

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

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

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

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

Session Resumption

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

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

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 的過程:

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

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 來驗證:

可以看到,服務端在傳送完證書後,緊接著又發來了它的 OCSP 響應,從而避免了瀏覽器自己去驗證證書造成阻塞。需要注意的是,OCSP Response 只能包含一個證書的驗證結果,瀏覽器還是可能自己去驗證中間證書。另外,OCSP Response 本身會佔用幾 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 效能權威指南》。

相關文章