字節(jié)二面:優(yōu)化 HTTPS 的手段,你知道幾個?
由裸數(shù)據(jù)傳輸?shù)?HTTP 協(xié)議轉(zhuǎn)成加密數(shù)據(jù)傳輸?shù)?HTTPS 協(xié)議,給應(yīng)用數(shù)據(jù)套了個「保護(hù)傘」,提高安全性的同時也帶來了性能消耗。
因為 HTTPS 相比 HTTP 協(xié)議多一個 TLS 協(xié)議握手過程,目的是為了通過非對稱加密握手協(xié)商或者交換出對稱加密密鑰,這個過程最長可以花費掉 2 RTT,接著后續(xù)傳輸?shù)膽?yīng)用數(shù)據(jù)都得使用對稱加密密鑰來加密/解密。
為了數(shù)據(jù)的安全性,我們不得不使用 HTTPS 協(xié)議,至今大部分網(wǎng)址都已從 HTTP 遷移至 HTTPS 協(xié)議,因此針對 HTTPS 的優(yōu)化是非常重要的。
這次,就從多個角度來優(yōu)化 HTTPS。
分析性能損耗
既然要對 HTTPS 優(yōu)化,那得清楚哪些步驟會產(chǎn)生性能消耗,再對癥下藥。
產(chǎn)生性能消耗的兩個環(huán)節(jié):
-
第一個環(huán)節(jié), TLS 協(xié)議握手過程;
-
第二個環(huán)節(jié),握手后的對稱加密報文傳輸。
對于第二環(huán)節(jié),現(xiàn)在主流的對稱加密算法 AES、ChaCha20 性能都是不錯的,而且一些 CPU 廠商還針對它們做了硬件級別的優(yōu)化,因此這個環(huán)節(jié)的性能消耗可以說非常地小。
而第一個環(huán)節(jié),TLS 協(xié)議握手過程不僅增加了網(wǎng)絡(luò)延時(最長可以花費掉 2 RTT),而且握手過程中的一些步驟也會產(chǎn)生性能損耗,比如:
-
對于 ECDHE 密鑰協(xié)商算法,握手過程中會客戶端和服務(wù)端都需要臨時生成橢圓曲線公私鑰;
-
客戶端驗證證書時,會訪問 CA 獲取 CRL 或者 OCSP,目的是驗證服務(wù)器的證書是否有被吊銷;
-
雙方計算 Pre-Master,也就是會話密鑰;
為了大家更清楚這些步驟在 TLS 協(xié)議握手的哪一個階段,我畫出了這幅圖:
硬件優(yōu)化
玩游戲時,如果我們怎么都戰(zhàn)勝不了對方,那么有一個最有效、最快的方式來變強(qiáng),那就是「充錢」,如果還是不行,那說明你充的錢還不夠多。
對于計算機(jī)里也是一樣,軟件都是跑在物理硬件上,硬件越牛逼,軟件跑的也越快,所以如果要優(yōu)化 HTTPS 優(yōu)化,最直接的方式就是花錢買性能參數(shù)更牛逼的硬件。
但是花錢也要花對方向,HTTPS 協(xié)議是計算密集型,而不是 I/O 密集型,所以不能把錢花在網(wǎng)卡、硬盤等地方,應(yīng)該花在 CPU 上。
一個好的 CPU,可以提高計算性能,因為 HTTPS 連接過程中就有大量需要計算密鑰的過程,所以這樣可以加速 TLS 握手過程。
另外,如果可以,應(yīng)該選擇可以支持 AES-NI 特性的 CPU,因為這種款式的 CPU 能在指令級別優(yōu)化了 AES 算法,這樣便加速了數(shù)據(jù)的加解密傳輸過程。
如果你的服務(wù)器是 Linux 系統(tǒng),那么你可以使用下面這行命令查看 CPU 是否支持 AES-NI 指令集:
如果我們的 CPU 支持 AES-NI 特性,那么對于對稱加密的算法應(yīng)該選擇 AES 算法。否則可以選擇 ChaCha20 對稱加密算法,因為 ChaCha20 算法的運算指令相比 AES 算法會對 CPU 更友好一點。
軟件優(yōu)化
如果公司預(yù)算充足對于新的服務(wù)器是可以考慮購買更好的 CPU,但是對于已經(jīng)在使用的服務(wù)器,硬件優(yōu)化的方式可能就不太適合了,于是就要從軟件的方向來優(yōu)化了。
軟件的優(yōu)化方向可以分層兩種,一個是軟件升級,一個是協(xié)議優(yōu)化。
先說第一個軟件升級,軟件升級就是將正在使用的軟件升級到最新版本,因為最新版本不僅提供了最新的特性,也優(yōu)化了以前軟件的問題或性能。比如:
-
將 Linux 內(nèi)核從 2.x 升級到 4.x;
-
將 OpenSSL 從 1.0.1 升級到 1.1.1;
-
…
看似簡單的軟件升級,對于有成百上千服務(wù)器的公司來說,軟件升級也跟硬件升級同樣是一個棘手的問題,因為要實行軟件升級,會花費時間和人力,同時也存在一定的風(fēng)險,也可能會影響正常的線上服務(wù)。
既然如此,我們把目光放到協(xié)議優(yōu)化,也就是在現(xiàn)有的環(huán)節(jié)下,通過較小的改動,來進(jìn)行優(yōu)化。
協(xié)議優(yōu)化
協(xié)議的優(yōu)化就是對「密鑰交換過程」進(jìn)行優(yōu)化。
密鑰交換算法優(yōu)化
TLS 1.2 版本如果使用的是 RSA 密鑰交換算法,那么需要 4 次握手,也就是要花費 2 RTT,才可以進(jìn)行應(yīng)用數(shù)據(jù)的傳輸,而且 RSA 密鑰交換算法不具備前向安全性。
總之使用 RSA 密鑰交換算法的 TLS 握手過程,不僅慢,而且安全性也不高。
因此如果可以,盡量選用 ECDHE 密鑰交換算法替換 RSA 算法,因為該算法由于支持「False Start」,它是“搶跑”的意思,客戶端可以在 TLS 協(xié)議的第 3 次握手后,第 4 次握手前,發(fā)送加密的應(yīng)用數(shù)據(jù),以此將 TLS 握手的消息往返由 2 RTT 減少到 1 RTT,而且安全性也高,具備前向安全性。
ECDHE 算法是基于橢圓曲線實現(xiàn)的,不同的橢圓曲線性能也不同,應(yīng)該盡量選擇 x25519 曲線,該曲線是目前最快的橢圓曲線。
比如在 Nginx 上,可以使用 ssl_ecdh_curve 指令配置想使用的橢圓曲線,把優(yōu)先使用的放在前面:
對于對稱加密算法方面,如果對安全性不是特別高的要求,可以選用 AES_128_GCM,它比 AES_256_GCM 快一些,因為密鑰的長度短一些。
比如在 Nginx 上,可以使用 ssl_ciphers 指令配置想使用的非對稱加密算法和對稱加密算法,也就是密鑰套件,而且把性能最快最安全的算法放在最前面:
TLS 升級
當(dāng)然,如果可以,直接把 TLS 1.2 升級成 TLS 1.3,TLS 1.3 大幅度簡化了握手的步驟,完成 TLS 握手只要 1 RTT,而且安全性更高。
在 TLS 1.2 的握手中,一般是需要 4 次握手,先要通過 Client Hello (第 1 次握手)和 Server Hello(第 2 次握手) 消息協(xié)商出后續(xù)使用的加密算法,再互相交換公鑰(第 3 和 第 4 次握手),然后計算出最終的會話密鑰,下圖的左邊部分就是 TLS 1.2 的握手過程:
上圖的右邊部分就是 TLS 1.3 的握手過程,可以發(fā)現(xiàn) TLS 1.3 把 Hello 和公鑰交換這兩個消息合并成了一個消息,于是這樣就減少到只需 1 RTT 就能完成 TLS 握手。
怎么合并的呢?具體的做法是,客戶端在 Client Hello 消息里帶上了支持的橢圓曲線,以及這些橢圓曲線對應(yīng)的公鑰。
服務(wù)端收到后,選定一個橢圓曲線等參數(shù),然后返回消息時,帶上服務(wù)端這邊的公鑰。經(jīng)過這 1 個 RTT,雙方手上已經(jīng)有生成會話密鑰的材料了,于是客戶端計算出會話密鑰,就可以進(jìn)行應(yīng)用數(shù)據(jù)的加密傳輸了。
而且,TLS1.3 對密碼套件進(jìn)行“減肥”了,對于密鑰交換算法,廢除了不支持前向安全性的 RSA 和 DH 算法,只支持 ECDHE 算法。
對于對稱加密和簽名算法,只支持目前最安全的幾個密碼套件,比如 openssl 中僅支持下面 5 種密碼套件:
-
TLS_AES_256_GCM_SHA384
-
TLS_CHACHA20_POLY1305_SHA256
-
TLS_AES_128_GCM_SHA256
-
TLS_AES_128_CCM_8_SHA256
-
TLS_AES_128_CCM_SHA256
之所以 TLS1.3 僅支持這么少的密碼套件,是因為 TLS1.2 由于支持各種古老且不安全的密碼套件,中間人可以利用降級攻擊,偽造客戶端的 Client Hello 消息,替換客戶端支持的密碼套件為一些不安全的密碼套件,使得服務(wù)器被迫使用這個密碼套件進(jìn)行 HTTPS 連接,從而破解密文。
證書優(yōu)化
為了驗證的服務(wù)器的身份,服務(wù)器會在 TSL 握手過程中,把自己的證書發(fā)給客戶端,以此證明自己身份是可信的。
對于證書的優(yōu)化,可以有兩個方向:
-
一個是證書傳輸,
-
一個是證書驗證;
證書傳輸優(yōu)化
要讓證書更便于傳輸,那必然是減少證書的大小,這樣可以節(jié)約帶寬,也能減少客戶端的運算量。所以,對于服務(wù)器的證書應(yīng)該選擇 橢圓曲線(ECDSA)證書,而不是 RSA 證書,因為在相同安全強(qiáng)度下, ECC 密鑰長度比 RSA 短的多。
證書驗證優(yōu)化
客戶端在驗證證書時,是個復(fù)雜的過程,會走證書鏈逐級驗證,驗證的過程不僅需要「用 CA 公鑰解密證書」以及「用簽名算法驗證證書的完整性」,而且為了知道證書是否被 CA 吊銷,客戶端有時還會再去訪問 CA, 下載 CRL 或者 OCSP 數(shù)據(jù),以此確認(rèn)證書的有效性。
這個訪問過程是 HTTP 訪問,因此又會產(chǎn)生一系列網(wǎng)絡(luò)通信的開銷,如 DNS 查詢、建立連接、收發(fā)數(shù)據(jù)等。
CRL
CRL 稱為證書吊銷列表(Certificate Revocation List),這個列表是由 CA 定期更新,列表內(nèi)容都是被撤銷信任的證書序號,如果服務(wù)器的證書在此列表,就認(rèn)為證書已經(jīng)失效,不在的話,則認(rèn)為證書是有效的。
但是 CRL 存在兩個問題:
-
第一個問題,由于 CRL 列表是由 CA 維護(hù)的,定期更新,如果一個證書剛被吊銷后,客戶端在更新 CRL 之前還是會信任這個證書,實時性較差;
-
第二個問題,隨著吊銷證書的增多,列表會越來越大,下載的速度就會越慢,下載完客戶端還得遍歷這么大的列表,那么就會導(dǎo)致客戶端在校驗證書這一環(huán)節(jié)的延時很大,進(jìn)而拖慢了 HTTPS 連接。
OCSP
因此,現(xiàn)在基本都是使用 OCSP ,名為在線證書狀態(tài)協(xié)議(Online Certificate Status Protocol)來查詢證書的有效性,它的工作方式是向 CA 發(fā)送查詢請求,讓 CA 返回證書的有效狀態(tài)。
不必像 CRL 方式客戶端需要下載大大的列表,還要從列表查詢,同時因為可以實時查詢每一張證書的有效性,解決了 CRL 的實時性問題。
OCSP 需要向 CA 查詢,因此也是要發(fā)生網(wǎng)絡(luò)請求,而且還得看 CA 服務(wù)器的“臉色”,如果網(wǎng)絡(luò)狀態(tài)不好,或者 CA 服務(wù)器繁忙,也會導(dǎo)致客戶端在校驗證書這一環(huán)節(jié)的延時變大。
OCSP Stapling
于是為了解決這一個網(wǎng)絡(luò)開銷,就出現(xiàn)了 OCSP Stapling,其原理是:服務(wù)器向 CA 周期性地查詢證書狀態(tài),獲得一個帶有時間戳和簽名的響應(yīng)結(jié)果并緩存它。
當(dāng)有客戶端發(fā)起連接請求時,服務(wù)器會把這個「響應(yīng)結(jié)果」在 TLS 握手過程中發(fā)給客戶端。由于有簽名的存在,服務(wù)器無法篡改,因此客戶端就能得知證書是否已被吊銷了,這樣客戶端就不需要再去查詢。
會話復(fù)用
TLS 握手的目的就是為了協(xié)商出會話密鑰,也就是對稱加密密鑰,那我們?nèi)绻覀儼咽状?TLS 握手協(xié)商的對稱加密密鑰緩存起來,待下次需要建立 HTTPS 連接時,直接「復(fù)用」這個密鑰,不就減少 TLS 握手的性能損耗了嗎?
這種方式就是會話復(fù)用(TLS session resumption),會話復(fù)用分兩種:
-
第一種叫 Session ID;
-
第二種叫 Session Ticket;
Session ID
Session ID 的工作原理是,客戶端和服務(wù)器首次 TLS 握手連接后,雙方會在內(nèi)存緩存會話密鑰,并用唯一的 Session ID 來標(biāo)識,Session ID 和會話密鑰相當(dāng)于 key-value 的關(guān)系。
當(dāng)客戶端再次連接時,hello 消息里會帶上 Session ID,服務(wù)器收到后就會從內(nèi)存找,如果找到就直接用該會話密鑰恢復(fù)會話狀態(tài),跳過其余的過程,只用一個消息往返就可以建立安全通信。當(dāng)然為了安全性,內(nèi)存中的會話密鑰會定期失效。
但是它有兩個缺點:
-
服務(wù)器必須保持每一個客戶端的會話密鑰,隨著客戶端的增多,服務(wù)器的內(nèi)存壓力也會越大。
-
現(xiàn)在網(wǎng)站服務(wù)一般是由多臺服務(wù)器通過負(fù)載均衡提供服務(wù)的,客戶端再次連接不一定會命中上次訪問過的服務(wù)器,于是還要走完整的 TLS 握手過程;
Session Ticket
為了解決 Session ID 的問題,就出現(xiàn)了 Session Ticket,服務(wù)器不再緩存每個客戶端的會話密鑰,而是把緩存的工作交給了客戶端,類似于 HTTP 的 Cookie。
客戶端與服務(wù)器首次建立連接時,服務(wù)器會加密「會話密鑰」作為 Ticket 發(fā)給客戶端,交給客戶端緩存該 Ticket。
客戶端再次連接服務(wù)器時,客戶端會發(fā)送 Ticket,服務(wù)器解密后就可以獲取上一次的會話密鑰,然后驗證有效期,如果沒問題,就可以恢復(fù)會話了,開始加密通信。
對于集群服務(wù)器的話,要確保每臺服務(wù)器加密 「會話密鑰」的密鑰是一致的,這樣客戶端攜帶 Ticket 訪問任意一臺服務(wù)器時,都能恢復(fù)會話。
Session ID 和 Session Ticket 都不具備前向安全性,因為一旦加密「會話密鑰」的密鑰被破解或者服務(wù)器泄漏「會話密鑰」,前面劫持的通信密文都會被破解。
同時應(yīng)對重放攻擊也很困難,這里簡單介紹下重放攻擊工作的原理。
假設(shè) Alice 想向 Bob 證明自己的身份。Bob 要求 Alice 的密碼作為身份證明,愛麗絲應(yīng)盡全力提供(可能是在經(jīng)過如哈希函數(shù)的轉(zhuǎn)換之后)。與此同時,Eve 竊聽了對話并保留了密碼(或哈希)。
交換結(jié)束后,Eve(冒充 Alice )連接到 Bob。當(dāng)被要求提供身份證明時,Eve 發(fā)送從 Bob 接受的最后一個會話中讀取的 Alice 的密碼(或哈希),從而授予 Eve 訪問權(quán)限。
重放攻擊的危險之處在于,如果中間人截獲了某個客戶端的 Session ID 或 Session Ticket 以及 POST 報文,而一般 POST 請求會改變數(shù)據(jù)庫的數(shù)據(jù),中間人就可以利用此截獲的報文,不斷向服務(wù)器發(fā)送該報文,這樣就會導(dǎo)致數(shù)據(jù)庫的數(shù)據(jù)被中間人改變了,而客戶是不知情的。
避免重放攻擊的方式就是需要對會話密鑰設(shè)定一個合理的過期時間。
Pre-shared Key
前面的 Session ID 和 Session Ticket 方式都需要在 1 RTT 才能恢復(fù)會話。
而 TLS1.3 更為牛逼,對于重連 TLS1.3 只需要 0 RTT,原理和 Ticket 類似,只不過在重連時,客戶端會把 Ticket 和 HTTP 請求一同發(fā)送給服務(wù)端,這種方式叫 Pre-shared Key。
同樣的,Pre-shared Key 也有重放攻擊的危險。
如上圖,假設(shè)中間人通過某種方式,截獲了客戶端使用會話重用技術(shù)的 POST 請求,通常 POST 請求是會改變數(shù)據(jù)庫的數(shù)據(jù),然后中間人就可以把截獲的這個報文發(fā)送給服務(wù)器,服務(wù)器收到后,也認(rèn)為是合法的,于是就恢復(fù)會話,致使數(shù)據(jù)庫的數(shù)據(jù)又被更改,但是此時用戶是不知情的。所以,應(yīng)對重放攻擊可以給會話密鑰設(shè)定一個合理的過期時間,以及只針對安全的 HTTP 請求如 GET/HEAD 使用會話重用。
總結(jié)
對于硬件優(yōu)化的方向,因為 HTTPS 是屬于計算密集型,應(yīng)該選擇計算力更強(qiáng)的 CPU,而且最好選擇支持 AES-NI 特性的 CPU,這個特性可以在硬件級別優(yōu)化 AES 對稱加密算法,加快應(yīng)用數(shù)據(jù)的加解密。
對于軟件優(yōu)化的方向,如果可以,把軟件升級成較新的版本,比如將 Linux 內(nèi)核 2.X 升級成 4.X,將 openssl 1.0.1 升級到 1.1.1,因為新版本的軟件不僅會提供新的特性,而且還會修復(fù)老版本的問題。
對于協(xié)議優(yōu)化的方向:
-
密鑰交換算法應(yīng)該選擇 ECDHE 算法,而不用 RSA 算法,因為 ECDHE 算法具備前向安全性,而且客戶端可以在第三次握手之后,就發(fā)送加密應(yīng)用數(shù)據(jù),節(jié)省了 1 RTT。
-
將 TSL1.2 升級 TSL1.3,因為 TSL1.3 的握手過程只需要 1 RTT,而且安全性更強(qiáng)。
對于證書優(yōu)化的方向:
-
服務(wù)器應(yīng)該選用 ECDSA 證書,而非 RSA 證書,因為在相同安全級別下,ECC 的密鑰長度比 RSA 短很多,這樣可以提高證書傳輸?shù)男剩?
-
服務(wù)器應(yīng)該開啟 OCSP Stapling 功能,由服務(wù)器預(yù)先獲得 OCSP 的響應(yīng),并把響應(yīng)結(jié)果緩存起來,這樣 TLS 握手的時候就不用再訪問 CA 服務(wù)器,減少了網(wǎng)絡(luò)通信的開銷,提高了證書驗證的效率;
對于重連 HTTPS 時,我們可以使用一些技術(shù)讓客戶端和服務(wù)端使用上一次 HTTPS 連接使用的會話密鑰,直接恢復(fù)會話,而不用再重新走完整的 TLS 握手過程。
常見的會話重用技術(shù)有 Session ID 和 Session Ticket,用了會話重用技術(shù),當(dāng)再次重連 HTTPS 時,只需要 1 RTT 就可以恢復(fù)會話。對于 TLS1.3 使用 Pre-shared Key 會話重用技術(shù),只需要 0 RTT 就可以恢復(fù)會話。
這些會話重用技術(shù)雖然好用,但是存在一定的安全風(fēng)險,它們不僅不具備前向安全,而且有重放攻擊的風(fēng)險,所以應(yīng)當(dāng)對會話密鑰設(shè)定一個合理的過期時間。
巨人的肩膀
-
http://www.doc88.com/p-8621583210895.html
-
https://zhuanlan.zhihu.com/p/33685085
-
https://en.wikipedia.org/wiki/Replay_attack
-
https://en.wikipedia.org/wiki/Downgrade_attack
-
https://www.cnblogs.com/racent-Z/p/14011056.html
-
http://www.guoyanbin.com/a-detailed-look-at-rfc-8446-a-k-a-tls-1-3/
-
https://www.thesslstore.com/blog/crl-explained-what-is-a-certificate-revocation-list/
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!