TLS握手優化詳解

 

 

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

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

TLS 握手

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

 

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

詳細的 TLS 握手過程這裏就不介紹了,你們能夠經過這兩篇文章去了解:緩存

經過 Wireshark 抓包能夠清楚地看到完整 TLS 握手過程所需的兩個 RTT,以下圖:服務器

 

False Start

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

 

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

  • 服務端必須在 Server Hello 握手中經過 NPN(Next Protocol Negotiation,下一代協議協商,Google 在 SPDY 協議中開發的 TLS 擴展,用於握手階段協商應用協議)或 ALPN(Application Layer Protocol Negotiation,應用層協議協商,NPN 的官方修訂版)代表本身支持的 HTTP 協議,例如:http/1.一、http/2;

  • 使用支持前向安全性(Forward Secrecy)的加密算法。False Start 在還沒有完成握手時就發送了應用數據,Forward Secrecy 能夠提升安全性;

經過 Wireshark 抓包能夠清楚地看到 False Start 帶來的好處(服務端的 ChangeCipherSpec 出如今 158 號包中,但在以前的 155 號包中,客戶端已經發出了請求,至關於 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,在確保安全性的同時,體積大幅減少。下面是一個對比:

對稱加密 Key 長度 RSA Key 長度 ECC Key 長度
80 1024 160
112 2048 224
128 3072 256
192 7680 384
256 15360 521

若是證書提供商支持 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 減小到一個,以下圖所示:

 

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

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 機制生效時的握手狀況,能夠看到沒有發送證書等環節:

 

測試

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.git
cd 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 106.187.88.156

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

[√] Check arguments.
[√] Solve 114.215.116.12:
    │ Got 1 result:
    │ 114.215.116.12
[√] Solve 106.187.88.156:
    │ Got 1 result:
    │ 106.187.88.156
[√] Using SNI name imququ.com.
[√] Prepare tests.
[√] Run tests without use of tickets.
[√] Display result set:
    │   IP address   │ Try │ Reuse │ SSL Session ID │ Master key │ Ticket
    │ ───────────────┼─────┼───────┼────────────────┼────────────┼────────
    │ 114.215.116.12 │  0  │   ×   │ BAF0834EA3896… │ 132A7A2DC… │   ×   
    │ 114.215.116.12 │  1  │   √   │ BAF0834EA3896… │ 132A7A2DC… │   ×   
    │ 114.215.116.12 │  2  │   √   │ BAF0834EA3896… │ 132A7A2DC… │   ×   
    │ 114.215.116.12 │  3  │   √   │ BAF0834EA3896… │ 132A7A2DC… │   ×   
    │ 114.215.116.12 │  4  │   √   │ BAF0834EA3896… │ 132A7A2DC… │   ×   
    │ 106.187.88.156 │  0  │   ×   │ 2F8143213E3B9… │ 1BFE00946… │   ×   
    │ 106.187.88.156 │  1  │   √   │ 2F8143213E3B9… │ 1BFE00946… │   ×   
    │ 106.187.88.156 │  2  │   √   │ 2F8143213E3B9… │ 1BFE00946… │   ×   
    │ 106.187.88.156 │  3  │   √   │ 2F8143213E3B9… │ 1BFE00946… │   ×   
    │ 106.187.88.156 │  4  │   √   │ 2F8143213E3B9… │ 1BFE00946… │   ×   
[√] Dump results to file.
[√] Run tests with use of tickets.
[√] Display result set:
    │   IP address   │ Try │ Reuse │ SSL Session ID │ Master key │ Ticket
    │ ───────────────┼─────┼───────┼────────────────┼────────────┼────────
    │ 114.215.116.12 │  0  │   ×   │ 96A21A1849BD4… │ C15030CF8… │   √   
    │ 114.215.116.12 │  1  │   √   │ 96A21A1849BD4… │ C15030CF8… │   √   
    │ 114.215.116.12 │  2  │   √   │ 96A21A1849BD4… │ C15030CF8… │   √   
    │ 114.215.116.12 │  3  │   √   │ 96A21A1849BD4… │ C15030CF8… │   √   
    │ 114.215.116.12 │  4  │   √   │ 96A21A1849BD4… │ C15030CF8… │   √   
    │ 106.187.88.156 │  0  │   √   │ 96A21A1849BD4… │ C15030CF8… │   √   
    │ 106.187.88.156 │  1  │   √   │ 96A21A1849BD4… │ C15030CF8… │   √   
    │ 106.187.88.156 │  2  │   √   │ 96A21A1849BD4… │ C15030CF8… │   √   
    │ 106.187.88.156 │  3  │   √   │ 96A21A1849BD4… │ C15030CF8… │   √   
    │ 106.187.88.156 │  4  │   √   │ 96A21A1849BD4… │ C15030CF8… │   √   
[√] Dump results to file.

從以上結果能夠看出:禁用 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 來驗證:

 

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

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

 

來源:Jerry Qu的小站

原文:http://t.cn/RUCdjKs

版權:本文版權歸原做者全部

相關文章
相關標籤/搜索