關於啟用 HTTPS 的一些經驗分享(二)

發表於2015-12-24

幾天前,一位朋友問我:都說推薦用 Qualys SSL Labs 這個工具測試 SSL 安全性,為什麼有些安全實力很強的大廠家評分也很低?我認為這個問題應該從兩方面來看:1)國內使用者終端情況複雜,很多時候降低 SSL 安全配置是為了相容更多使用者;2)確實有一些大廠家的 SSL 配置很不專業,尤其是配置了一些明顯不該使用的 CipherSuite。

我之前寫的《關於啟用 HTTPS 的一些經驗分享(一)》,主要介紹 HTTPS 如何與一些新出的安全規範配合使用,面向的是現代瀏覽器。而今天這篇文章,更多的是介紹啟用 HTTPS 過程中在老舊瀏覽器下可能遇到的問題,以及如何取捨。

SSL 版本選擇

TLS(Transport Layer Security,傳輸層安全)的前身是 SSL(Secure Sockets Layer,安全套接字層),它最初的幾個版本(SSL 1.0、SSL 2.0、SSL 3.0)由網景公司開發,從 3.1 開始被 IETF 標準化並改名,發展至今已經有 TLS 1.0、TLS 1.1、TLS 1.2 三個版本。TLS 1.3 改動會比較大,目前還在草案階段。

SSL 1.0 從未公開過,而 SSL 2.0 和 SSL 3.0 都存在安全問題,不推薦使用。Nginx 從 1.9.1 開始預設只支援 TLS 的三個版本,以下是 Nginx 官方文件中對 ssl_protocols 配置的說明:

Syntax: ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2];
Default: ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
Context: http, server
Enables the specified protocols. The TLSv1.1 and TLSv1.2 parameters work only when the OpenSSL library of version 1.0.1 or higher is used.

但不幸的是,IE 6 只支援 SSLv2 和 SSLv3(來源),也就是說 HTTPS 網站要支援 IE 6,就必須啟用 SSLv3。僅這一項就會導致 SSL Labs 給出的評分降為 C。

加密套件選擇

加密套件(CipherSuite),是在 SSL 握手中需要協商的很重要的一個引數。客戶端會在 Client Hello 中帶上它所支援的 CipherSuite 列表,服務端會從中選定一個並通過 Server Hello 返回。如果客戶端支援的 CipherSuite 列表與服務端配置的 CipherSuite 列表沒有交集,會導致無法完成協商,握手失敗。

CipherSuite 包含多種技術,例如認證演算法(Authentication)、加密演算法(Encryption)、訊息認證碼演算法(Message Authentication Code,簡稱為 MAC)、金鑰交換演算法(Key Exchange)和金鑰衍生演算法(Key Derivation Function)。

SSL 的 CipherSuite 協商機制具有良好的擴充套件性,每個 CipherSuite 都需要在 IANA 註冊,並被分配兩個位元組的標誌。全部 CipherSuite 可以在 IANA 的 TLS Cipher Suite Registry 頁面檢視。

OpenSSL 庫支援的全部 CipherSuite 可以通過以下命令檢視:

0xCC,0x14 是 CipherSuite 的編號,在 SSL 握手中會用到。ECDHE-ECDSA-CHACHA20-POLY1305 是它的名稱,之後幾部分分別表示:用於 TLSv1.2,使用 ECDH 做金鑰交換,使用 ECDSA 做認證,使用 ChaCha20-Poly1305 做對稱加密,由於 ChaCha20-Poly1305 是一種 AEAD 模式,不需要 MAC 演算法,所以 MAC 列顯示為 AEAD。

要了解 CipherSuite 的更多內容,可以閱讀這篇長文《TLS 協議分析 與 現代加密通訊協議設計》。總之,在配置 CipherSuite 時,請務必參考權威文件,如:Mozilla 的推薦配置CloudFlare 使用的配置

以上 Mozilla 文件中的「Old backward compatibility」配置,以及 CloudFlare 的配置,都可以很好的相容老舊瀏覽器,包括 Windows XP / IE6。

之前見到某個大廠家居然支援包含 EXPORT 的 CipherSuite,這些套件在上世紀由於美國出口限制而被弱化過,已被攻破,實在沒有理由再使用。

SNI 擴充套件

我們知道,在 Nginx 中可以通過指定不同的 server_name 來配置多個站點。HTTP/1.1 協議請求頭中的 Host 欄位可以標識出當前請求屬於哪個站點。但是對於 HTTPS 網站來說,要想傳送 HTTP 資料,必須等待 SSL 握手完成,而在握手階段服務端就必須提供網站證書。對於在同一個 IP 部署不同 HTTPS 站點,並且還使用了不同證書的情況下,服務端怎麼知道該傳送哪個證書?

Server Name Indication,簡稱為 SNI,是 TLS 的一個擴充套件,為解決這個問題應運而生。有了 SNI,服務端可以通過 Client Hello 中的 SNI 擴充套件拿到使用者要訪問網站的 Server Name,進而傳送與之匹配的證書,順利完成 SSL 握手。

Nginx 在很早之前就支援了 SNI,可以通過 nginx -V 來驗證。以下是我的驗證結果:

然而,並不是所有瀏覽器都支援 SNI,以下是常見瀏覽器支援 SNI 的最低版本:

瀏覽器 最低版本
Chrome Vista+ 全支援;XP 需要 Chrome 6+;OSX 10.5.7+ 且 Chrome 5+
Firefox 2.0+
Internet Explorer 7+ (需要 Vista+)
Safari 3+ (需要 OS X 10.5.6+)
Mobile Safari iOS 4.0+
Android Webview 3.0+

如果要避免在不支援 SNI 的瀏覽器中出現證書錯誤,只能將使用不同證書的 HTTPS 站點部署在不同 IP 上,最簡單的做法是分開部署到不同機器上。

證書選擇

HTTPS 網站需要通過 CA 取得合法證書,證書通過數字簽名技術確保第三方無法偽造。證書的簡單原理如下:

  • 根據版本號、序列號、簽名演算法標識、發行者名稱、有效期、證書主體名、證書主體公鑰資訊、發行商唯一標識、主體唯一標識、擴充套件生成 TBSCertificate(To Be Signed Certificate, 待簽名證書)資訊;
  • 簽發數字簽名:使用 HASH 函式對 TBSCertificate 計算得到訊息摘要,用 CA 的私鑰對訊息摘要進行加密,得到簽名;
  • 校驗數字簽名:使用相同的 HASH 函式對 TBSCertificate 計算得到訊息摘要,與使用 CA 公鑰解密簽名得到內容相比較;

使用 SHA-1 做為 HASH 函式的證書被稱之為 SHA-1 證書,由於目前已經找到 SHA-1 的碰撞條件,將證書換成使用更安全的 SHA-2 做為 HASH 函式的 SHA-2 證書被提上日程。

實際上,微軟已經宣稱自 2017 年 1 月 1 日起,將全面停止對 SHA-1 證書的支援。屆時在最新版本的 Windows 系統中,SHA-1 證書將不被信任。

而根據 Chrome 官方部落格的文章,使用 SHA-1 證書且證書有效期在 2016 年 1 月 1 號至 2016 年 12 月 31 號之間的站點會被給予「安全的,但存在漏洞」的提示,也就是位址列的小鎖不再是綠色的,並且會有一個黃色小三角。而使用 SHA-1 證書且證書有效期超過 2017 年 1 月 1 號的站點會被給予「不安全」的紅色警告,小鎖上直接顯示一個紅色的叉。

然而,並不是所有的終端都支援 SHA-2 證書,服務端不支援還好辦,瀏覽器只能依賴於使用者升級了。下面是常見瀏覽器支援 SHA-2 證書的最低版本:

瀏覽器 支援 SHA-2 證書的最低版本
Chrome 26+
Firefox 1.5+
Internet Explorer 6+ (需要 XP SP3+)
Safari 3+ (需要 OS X 10.5+)
Android Webview 2.3+

可以看到,如果要照顧沒有打 XP SP3 補丁的 IE6 使用者,只能繼續使用 SHA-1 證書。

在我之前的文章中,還提到過 ECC 證書,這種新型的證書支援度更差,這裡略過不提,有興趣的同學可以點這裡檢視。

是否可以針對不同瀏覽器啟用不同證書呢?理論上服務端可以根據客戶端 Client Hello 中的 Cipher Suites 特徵以及是否支援 SNI 的特徵來分配不同證書,不過我沒有實際驗證過。

本文先寫這麼多,很多策略都需要根據自己網站的使用者來決定,例如我的部落格基本沒有 IE8- 使用者,理所當然可以禁用 SSLv3。如果你的產品還有很多使用老舊瀏覽器的使用者,那就必須為這些使用者做相容方案了。一種方案是:只把主域安全級別配低,將 XP 上 IE 使用者的 HTTPS 請求直接重定向到 HTTP 版本,這樣其它域名可以使用高安全級別的配置,運維起來比較方便。

相關文章