隨着 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 證書:
1
2
|
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 減少到一個,如下圖所示:
可以看到會話復用機制生效時,雙方幾乎不怎么交換數據就協商好密鑰了,這是怎么做到的呢?
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 性能權威指南》。