百度已經於近日上線了全站 HTTPS 的安全搜索,默認會將 HTTP 請求跳轉成 HTTPS。本文重點介紹 HTTPS 協議, 並簡單介紹部署全站 HTTPS 的意義。
HTTPS 可以認為是 HTTP + TLS。HTTP 協議大家耳熟能詳了,目前大部分 WEB 應用和網站都是使用 HTTP 協議傳輸的。
TLS 是傳輸層加密協議,它的前身是 SSL 協議,最早由 netscape 公司於 1995 年發布,1999 年經過 IETF 討論和規范後,改名為 TLS。如果沒有特別說明,SSL 和 TLS 說的都是同一個協議。
HTTP 和 TLS 在協議層的位置以及 TLS 協議的組成如下圖:
圖 1 TLS 協議格式
TLS 協議主要有五部分:應用數據層協議,握手協議,報警協議,加密消息確認協議,心跳協議。
TLS 協議本身又是由 record 協議傳輸的,record 協議的格式如上圖最右所示。
目前常用的 HTTP 協議是 HTTP1.1,常用的 TLS 協議版本有如下幾個:TLS1.2, TLS1.1, TLS1.0 和 SSL3.0。其中 SSL3.0 由於 POODLE 攻擊已經被證明不安全,但統計發現依然有不到 1% 的浏覽器使用 SSL3.0。TLS1.0 也存在部分安全漏洞,比如 RC4 和 BEAST 攻擊。
TLS1.2 和 TLS1.1 暫時沒有已知的安全漏洞,比較安全,同時有大量擴展提升速度和性能,推薦大家使用。
需要關注一點的就是 TLS1.3 將會是 TLS 協議一個非常重大的改革。不管是安全性還是用戶訪問速度都會有質的提升。不過目前沒有明確的發布時間。
同時 HTTP2 也已經正式定稿,這個由 SPDY 協議演化而來的協議相比 HTTP1.1 又是一個非常重大的變動,能夠明顯提升應用層數據的傳輸效率。
百度使用 HTTPS 協議主要是為了保護用戶隱私,防止流量劫持。
HTTP 本身是明文傳輸的,沒有經過任何安全處理。例如用戶在百度搜索了一個關鍵字,比如“蘋果手機”,中間者完全能夠查看到這個信息,並且有可能打電話過來騷擾 用戶。也有一些用戶投訴使用百度時,發現首頁或者結果頁面浮了一個很長很大的廣告,這也肯定是中間者往頁面插的廣告內容。如果劫持技術比較低劣的話,用戶 甚至無法訪問百度。
這裡提到的中間者主要指一些網絡節點,是用戶數據在浏覽器和百度服務器中間傳輸必須要經過的節點。比如 WIFI 熱點,路由器,防火牆,反向代理,緩存服務器等。
在 HTTP 協議下,中間者可以隨意嗅探用戶搜索內容,竊取隱私甚至篡改網頁。不過 HTTPS 是這些劫持行為的克星,能夠完全有效地防御。
總體來說,HTTPS 協議提供了三個強大的功能來對抗上述的劫持行為:
1, 內容加密。浏覽器到百度服務器的內容都是以加密形式傳輸,中間者無法直接查看原始內容。
2, 身份認證。保證用戶訪問的是百度服務,即使被 DNS 劫持到了第三方站點,也會提醒用戶沒有訪問百度服務,有可能被劫持
3, 數據完整性。防止內容被第三方冒充或者篡改。
那 HTTPS 是如何做到上述三點的呢?下面從原理角度介紹一下。
加密算法一般分為兩種,對稱加密和非對稱加密。所謂對稱加密(也叫密鑰加密)就是指加密和解密使用的是相同的密鑰。而非對稱加密(也叫公鑰加密)就是指加密和解密使用了不同的密鑰。
圖 2 對稱加密
圖 3 非對稱加密
對稱內容加密強度非常高,一般破解不了。但存在一個很大的問題就是無法安全地生成和保管密鑰。假如客戶端軟件和服務器之間每次會話都使用固定的,相 同的密鑰加密和解密,肯定存在很大的安全隱患。如果有人從客戶端端獲取到了對稱密鑰,整個內容就不存在安全性了,而且管理海量的客戶端密鑰也是一件很復雜 的事情。
非對稱加密主要用於密鑰交換(也叫密鑰協商),能夠很好地解決這個問題。浏覽器和服務器每次新建會話時都使用非對稱密鑰交換算法協商出對稱密鑰,使 用這些對稱密鑰完成應用數據的加解密和驗證,整個會話過程中的密鑰只在內存中生成和保存,而且每個會話的對稱密鑰都不相同(除非會話復用),中間者無法竊 取。
非對稱密鑰交換很安全,但同時也是 HTTPS 性能和速度嚴重降低的“罪魁禍首”。想要知道 HTTPS 為什麼影響速度,為什麼消耗資源,就一定要理解非對稱密鑰交換的整個過程。
下面重點介紹一下非對稱密鑰交換的數學原理及在 TLS 握手過程中的應用。
在非對稱密鑰交換算法出現以前,對稱加密一個很大的問題就是不知道如何安全生成和保管密鑰。非對稱密鑰交換過程主要就是為了解決這個問題,使得對稱密鑰的生成和使用更加安全。
密鑰交換算法本身非常復雜,密鑰交換過程涉及到隨機數生成,模指數運算,空白補齊,加密,簽名等操作。
常見的密鑰交換算法有 RSA,ECDHE,DH,DHE 等算法。它們的特性如下:
建議優先支持 RSA 和 ECDH_RSA 密鑰交換算法。原因是:
1, ECDHE 支持 ECC 加速,計算速度更快。支持 PFS,更加安全。支持 false start,用戶訪問速度更快。
2, 目前還有至少 20% 以上的客戶端不支持 ECDHE,我們推薦使用 RSA 而不是 DH 或者 DHE,因為 DH 系列算法非常消耗 CPU(相當於要做兩次 RSA 計算)。
需要注意通常所說的 ECDHE 密鑰交換默認都是指 ECDHE_RSA,使用 ECDHE 生成 DH 算法所需的公私鑰,然後使用 RSA 算法進行簽名最後再計算得出對稱密鑰。
非對稱加密相比對稱加密更加安全,但也存在兩個明顯缺點:
1, CPU 計算資源消耗非常大。一次完全 TLS 握手,密鑰交換時的非對稱解密計算量占整個握手過程的 90% 以上。而對稱加密的計算量只相當於非對稱加密的 0.1%,如果應用層數據也使用非對稱加解密,性能開銷太大,無法承受。
2, 非對稱加密算法對加密內容的長度有限制,不能超過公鑰長度。比如現在常用的公鑰長度是 2048 位,意味著待加密內容不能超過 256 個字節。
所以公鑰加密目前只能用來作密鑰交換或者內容簽名,不適合用來做應用層傳輸內容的加解密。
非對稱密鑰交換算法是整個 HTTPS 得以安全的基石,充分理解非對稱密鑰交換算法是理解 HTTPS 協議和功能的關鍵。
下面分別通俗地介紹一下 RSA 和 ECDHE 在密鑰交換過程中的應用。
RSA 算法的安全性是建立在乘法不可逆或者大數因子很難分解的基礎上。RSA 的推導和實現涉及到了歐拉函數和費馬定理及模反元素的概念,有興趣的讀者可以自行百度。
RSA 算法是統治世界的最重要算法之一,而且從目前來看,RSA 也是 HTTPS 體系中最重要的算法,沒有之一。
RSA 的計算步驟如下:
1, 隨機挑選兩個質數 p, q,假設 p = 13, q = 19。 n = p * q = 13 * 19 = 247;
2, ∅(n) 表示與整數 n 互質數的個數。如果 n 等於兩個質數的積,則∅(n)=(p-1)(q-1) 挑選一個數 e,滿足 1< e <∅(n) 並且 e 與互質,假設 e = 17;
3, 計算 e 關於 n 的模反元素, ed=1 mod ∅(n) , 由 e = 17 ,∅(n) =216 可得 d = 89;
4, 求出了 e,和 d,假設明文 m = 135,密文用 c 表示。那麼加解密計算如下:
實際應用中,(n,e) 組成了公鑰對,(n,d)組成了私鑰對,其中 n 和 d 都是一個接近 22048的大數。即使現在性能很強的 CPU,想要計算 m≡c^d mod(n),也需要消耗比較大的計算資源和時間。
公鑰對 (n, e) 一般都注冊到了證書裡,任何人都能直接查看,比如百度證書的公鑰對如下圖,其中最末 6 個數字(010001)換算成 10 進制就是 65537,也就是公鑰對中的 e。e 取值比較小的好處有兩個:
1, 由 c=m^e mod(n) 可知,e 較小,客戶端 CPU 計算消耗的資源較少。
2, 加大 server 端的破解難度。e 比較小,私鑰對中的 d 必然會非常大。所以 d 的取值空間也就非常大,增加了破解難度。
那為什麼 (n,e) 能做為公鑰公開,甚至大家都能直接從證書中查看到,這樣安全嗎?分析如下:
由於 ed≡1 mod ∅(n),知道了 e 和 n,想要求出私鑰 d,就必須知道∅(n)。而∅(n)=(p-1)*(q-1),必須計算出 p 和 q 才能確定私鑰 d。但是當 n 大到一定程度時(比如接近 2^2048),即使現在最快的 CPU 也無法進行這個因式分解,即無法知道 n 是由哪個數 p 和 q 乘出來的。所以就算知道了公鑰,整個加解密過程還是非常安全的。
圖 5 百度 HTTPS 證書公鑰
介紹完了 RSA 的原理,那最終會話所需要的對稱密鑰是如何生成的呢?跟 RSA 有什麼關系?
以 TLS1.2 為例簡單描述一下,省略跟密鑰交換無關的握手消息。過程如下:
1, 浏覽器發送 client_hello,包含一個隨機數 random1。
2, 服務端回復 server_hello,包含一個隨機數 random2,同時回復 certificate,攜帶了證書公鑰 P。
3, 浏覽器接收到 random2 之後就能夠生成 premaster_secrect 以及 master_secrect。其中 premaster_secret 長度為 48 個字節,前 2 個字節是協議版本號,剩下的 46 個字節填充一個隨機數。結構如下:
Struct {byte Version[2];bute random[46];}master secrect 的生成算法簡述如下:
Master_key = PRF(premaster_secret, “master secrect”, 隨機數1+隨機數2)其中 PRF 是一個隨機函數,定義如下:PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)從上式可以看出,把 premaster_key 賦值給 secret,”master key”賦值給 label,浏覽器和服務器端的兩個隨機數做種子就能確定地求出一個 48 位長的隨機數。
而 master secrect 包含了六部分內容,分別是用於校驗內容一致性的密鑰,用於對稱內容加解密的密鑰,以及初始化向量(用於 CBC 模式),客戶端和服務端各一份。
至此,浏覽器側的密鑰已經完成協商。
4, 浏覽器使用證書公鑰 P 將 premaster_secrect 加密後發送給服務器。
5, 服務端使用私鑰解密得到 premaster_secrect。又由於服務端之前就收到了隨機數 1,所以服務端根據相同的生成算法,在相同的輸入參數下,求出了相同的 master secrect。
RSA 密鑰協商握手過程圖示如下:
圖 6 RSA 密鑰協商過程
可以看出,密鑰協商過程需要 2 個 RTT,這也是 HTTPS 慢的一個重要原因。而 RSA 發揮的關鍵作用就是對 premaster_secrect 進行了加密和解密。中間者不可能破解 RSA 算法,也就不可能知道 premaster_secrect,從而保證了密鑰協商過程的安全性。
ECDHE 算法實現要復雜很多,主要分為兩部分:diffie-hellman 算法(簡稱為 DH)及 ECC(橢圓曲線算術)。他們的安全性都是建立在離散對數計算很困難的基礎上。
簡單介紹一下 dh 算法的實現,先介紹兩個基本概念:
b ≡ a^i mod p (0≤i≤p-1)
則稱 i 是 b 的以 a 為底的模 p 的離散對數。
理解這兩個概念,dh 算法就非常簡單了,示例如下:
假設 client 和 server 需要協商密鑰,p=2579,則本原根 a = 2。
1, Client 選擇隨機數 Kc = 123 做為自己的私鑰,計算 Yc = a^Kc mod p = 2^123 mod 2579 = 2400,把 Yc 作為公鑰發送給 server。
2, Server 選擇隨機數 Ks = 293 作為私鑰,計算 Ys = a^Ks mod p = s^293 mod 2579 = 968,把 Ys 作為公鑰發送給 client。
3, Client 計算共享密鑰:secrect = Ys^Kc mod (p) = 968^123 mod(2579) = 434
4, Server 計算共享密鑰:secrect = Yc^Ks mod(p) =2400^293 mod(2579) =434
上述公式中的 Ys,Yc,P, a, 都是公開信息,可以被中間者查看,只有 Ks,Kc 作為私鑰沒有公開,當私鑰較小時,通過窮舉攻擊能夠計算出共享密鑰,但是當私鑰非常大時,窮舉攻擊肯定是不可行的。
DH 算法有一個比較大的缺陷就是需要提供足夠大的私鑰來保證安全性,所以比較消耗 CPU 計算資源。ECC 橢圓曲線算術能夠很好的解決這個問題,224 位的密鑰長度就能達到 RSA2048 位的安全強度。
ECC 的曲線公式描述的其實不是橢圓,只是跟橢圓曲線周長公式形似才叫橢圓曲線加密算術。ECC 涉及到了有限域、群等近世代數的多個概念,就不做詳細介紹了。
ECC 安全性依賴於這樣一個事實:
P = kQ, 已知 k, Q 求出 P 相對簡單,但是已知 P 和 Q 求出 k 卻非常困難。上式看起來非常簡單,但有如下約束條件:
1, Q 是一個非常大的質數,p, k, q 都是橢圓曲線有限域上的離散點。
2, 有限域定義了自己的加法和乘法法則,即使 kQ 的運算也非常復雜。
ECC 應用於 Diffie-Hellman 密鑰交換過程如下:
1, 定義一個滿足橢圓方程的有限域,即挑選 p, a, b 滿足如下方程:
y^2 mod p = (x^3+ax +b) mod p2, 挑選基點 G = (x, y),G 的階為 n。n 為滿足 nG = 0 的最小正整數。
3, Client 選擇私鑰 Kc (0 <Kc<n ),產生公鑰 Yc =Kc *G
4, server 選擇私鑰 Ks 並產生公鑰 Ys =Ks*G
5, client 計算共享密鑰 K = Kc*Ys ,server 端計算共享密鑰 Ks*Yc ,這兩者的結果是一樣的,因為:
Kc*Ys = Kc*(Ks*G) = Ks*(Kc*G) = Ks*Yc由上面描述可知,只要確定 p, a, b 就能確定一條有限域上的橢圓曲線,由於不是所有的橢圓曲線都能夠用於加密,所以 p, a, b 的選取非常講究,直接關系曲線的安全性和計算速度。
Openssl 實現的,也是 FIPS 推薦的 256 位素數域上的橢圓曲線參數定義如下:
質數 p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 階 n = 115792089210356248762697446949407573529996955224135760342422259061068512044369SEED = c49d3608 86e70493 6a6678e1 139d26b7 819f7e90c = 7efba166 2985be94 03cb055c 75d4f7e0 ce8d84a9 c5114abcaf317768 0104fa0d 橢圓曲線的系數 a = 0 橢圓曲線的系統 b = 5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f63bce3c3e 27d2604b 基點 G x = 6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0f4a13945 d898c296 基點 G y = 4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ececbb64068 37bf51f5
簡單介紹了 ECC 和 DH 算法的數學原理,我們看下 ECDHE 在 TLS 握手過程中的應用。
相比 RSA,ECDHE 需要多發送一個 server_key_exchange 的握手消息才能完成密鑰協商。
同樣以 TLS1.2 為例,簡單描述一下過程:
1, 浏覽器發送 client_hello,包含一個隨機數 random1,同時需要有 2 個擴展:
a) Elliptic_curves:客戶端支持的曲線類型和有限域參數。現在使用最多的是 256 位的素數域,參數定義如上節所述。
b) Ec_point_formats:支持的曲線點格式,默認都是 uncompressed。
2, 服務端回復 server_hello,包含一個隨機數 random2 及 ECC 擴展。
3, 服務端回復 certificate,攜帶了證書公鑰。
4, 服務端生成 ECDH 臨時公鑰,同時回復 server_key_exchange,包含三部分重要內容:
a) ECC 相關的參數。
b) ECDH 臨時公鑰。
c) ECC 參數和公鑰生成的簽名值,用於客戶端校驗。
5, 浏覽器接收 server_key_exchange 之後,使用證書公鑰進行簽名解密和校驗,獲取服務器端的 ECDH 臨時公鑰,生成會話所需要的共享密鑰。
至此,浏覽器端完成了密鑰協商。
6, 浏覽器生成 ECDH 臨時公鑰和 client_key_exchange 消息,跟 RSA 密鑰協商不同的是,這個消息不需要加密了。
7, 服務器處理 client_key_exchang 消息,獲取客戶端 ECDH 臨時公鑰。
8, 服務器生成會話所需要的共享密鑰。
9, Server 端密鑰協商過程結束。
圖示如下:
圖 7 ECDHE 密鑰協商過程
非對稱密鑰交換過程結束之後就得出了本次會話需要使用的對稱密鑰。對稱加密又分為兩種模式:流式加密和分組加密。流式加密現在常用的就是 RC4,不過 RC4 已經不再安全,微軟也建議網站盡量不要使用 RC4 流式加密。
一種新的替代 RC4 的流式加密算法叫 ChaCha20,它是 google 推出的速度更快,更安全的加密算法。目前已經被 android 和 chrome 采用,也編譯進了 google 的開源 openssl 分支 —boring ssl,並且nginx 1.7.4 也支持編譯 boringssl。
分組加密以前常用的模式是 AES-CBC,但是 CBC 已經被證明容易遭受BEAST和LUCKY13 攻擊。目前建議使用的分組加密模式是 AES-GCM,不過它的缺點是計算量大,性能和電量消耗都比較高,不適用於移動電話和平板電腦。
身份認證主要涉及到 PKI 和數字證書。通常來講 PKI(公鑰基礎設施)包含如下部分:
申請一個受信任的數字證書通常有如下流程:
1, 終端實體生成公私鑰和證書請求。
2, RA 檢查實體的合法性。如果個人或者小網站,這一步不是必須的。
3, CA 簽發證書,發送給申請者。
4, 證書更新到 repository,終端後續從 repository 更新證書,查詢證書狀態等。
目前百度使用的證書是 X509v3 格式,由如下三個部分組成:
1, tbsCertificate(to be signed certificate 待簽名證書內容),這部分包含了 10 個要素,分別是版本號,序列號,簽名算法標識,發行者名稱,有效期,證書主體名,證書主體公鑰信息,發行商唯一標識,主體唯一標識,擴展等。
2, signatureAlgorithm,簽名算法標識,指定對 tbsCertificate 進行簽名的算法。
3, signaturValue(簽名值),使用 signatureAlgorithm 對 tbsCertificate 進行計算得到簽名值。
數字證書有兩個作用:
1, 身份授權。確保浏覽器訪問的網站是經過 CA 驗證的可信任的網站。
2, 分發公鑰。每個數字證書都包含了注冊者生成的公鑰。在 SSL 握手時會通過 certificate 消息傳輸給客戶端。比如前文提到的 RSA 證書公鑰加密及 ECDHE 的簽名都是使用的這個公鑰。
申請者拿到 CA 的證書並部署在網站服務器端,那浏覽器發起握手接收到證書後,如何確認這個證書就是 CA 簽發的呢?怎樣避免第三方偽造這個證書?
答案就是數字簽名(digital signature)。數字簽名是證書的防偽標簽,目前使用最廣泛的 SHA-RSA 數字簽名的制作和驗證過程如下:
1, 數字簽名的簽發。首先是使用哈希函數對待簽名內容進行安全哈希,生成消息摘要,然後使用 CA 自己的私鑰對消息摘要進行加密。
2, 數字簽名的校驗。使用 CA 的公鑰解密簽名,然後使用相同的簽名函數對待簽名證書內容進行簽名並和服務端數字簽名裡的簽名內容進行比較,如果相同就認為校驗成功。
圖 8 數字簽名生成及校驗
這裡有幾點需要說明:
這部分內容比較好理解,跟平時的 md5 簽名類似,只不過安全要求要高很多。openssl 現在使用的完整性校驗算法有兩種:MD5 或者 SHA。由於 MD5 在實際應用中存在沖突的可能性比較大,所以盡量別采用 MD5 來驗證內容一致性。SHA 也不能使用 SHA0 和 SHA1,中國山東大學的王小雲教授在 2005 年就宣布破解了 SHA-1 完整版算法。
微軟和 google 都已經宣布 16 年及 17 年之後不再支持 sha1 簽名證書。
HTTPS 目前唯一的問題就是它還沒有得到大規模應用,受到的關注和研究都比較少。至於使用成本和額外開銷,完全不用太過擔心。
一般來講,使用 HTTPS 前大家可能會非常關注如下問題:
數字證書的費用其實也不高,對於中小網站可以使用便宜甚至免費的數字證書服務(可能存在安全隱患),像著名的 verisign 公司的證書一般也就幾千到幾萬塊一年不等。當然如果公司對證書的需求比較大,定制性要求高,可以建立自己的 CA 站點,比如 google,能夠隨意簽發 google 相關證書。
大家現在使用百度 HTTPS 安全搜索,有感覺到慢嗎?
同樣地,只要合理優化,HTTPS 的機器成本也不會明顯增加。對於中小網站,完全不需要增加機器也能滿足性能需求。
國外的大型互聯網公司很多已經啟用了全站 HTTPS,這也是未來互聯網的趨勢。國內的大型互聯網並沒有全站部署 HTTPS,只是在一些涉及賬戶或者交易的子頁面 / 子請求上啟用了 HTTPS。百度搜索首次全站部署 HTTPS,對國內互聯網的全站 HTTPS 進程必將有著巨大的推動作用。
目前互聯網上關於 HTTPS 的中文資料比較少,本文就著重介紹了 HTTPS 協議涉及到的重要知識點和平時不太容易理解的盲區,希望能對大家理解 HTTPS 協議有幫助。百度 HTTPS 性能優化涉及到大量內容,從前端頁面、後端架構、協議特性、加密算法、流量調度、架構和運維、安全等方面都做了大量工作。