HTTPS 效能優化學習筆記
最近在學習https效能優化,雖然網上已經有許多的關於https效能優化的文章了,但還是想寫下這篇文章,作為學習總結=^_^=,文中對於一些概念性或實現細節上的東西並不會展開,但會給出相應的引用,有些圖片也來自網上資源。
章節規劃:
- 認識SSL/TLS
- 演算法選擇
- 會話恢復
- OCSP stapling
- TLS 緩衝區優化
- TLS false start
- 其他優化
認識SSL/TLS
SSL和TLS都是用於保障端到端之間連線的安全性。SSL最初是由Netscape開發的,後來為了使得該安全協議更加開放和自由,更名為TLS,並被標準化到RFC中,現在主流的是TLS 1.2版本。
從上圖,可以看出SSL/TLS是介於應用層和傳輸層之間,並且分為握手層(Handshake Layer)和記錄層(Record Layer)。
- 握手層:端與端之間協商密碼套件、連線狀態。
- 記錄層:對資料的封裝,資料交給傳輸層之前,會經過分片-壓縮-認證-加密。
從TLS 1.2 RFC可以瞭解更多:https://www.ietf.org/rfc/rfc5246.txt
演算法選擇
TLS中可被配置的演算法分類:
密碼套件決定了會使用到的演算法,例如執行openssl ciphers -v 'ALL' | grep ECDHE-RSA-AES128-GCM-SHA256
:
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD
表明該演算法是在TLS 1.2中支援的,金鑰交換採用ECDH(EC是指採用橢圓曲線的DH),數字簽名採用RSA,加密採用128位金鑰長度的AESGCM,訊息認證碼採用AEAD(AEAD是一種新的加密形式,把加密和訊息認證碼結合到一起,而不是某個演算法,例如使用AES並採用GCM模式加密,就能夠為資料提供保密性、完整性的保障)。
如何理解完整性?
A 將明文M加密後為MC,發給B,B解密,得到明文。 如果此時有中間人C,將MC替換為CMC(雖然C不知道A怎麼加密的,但這沒關係),B將CMC解密,得到明文(那麼B拿到的其實是錯誤的明文)。 所以需要引入訊息認證碼,B才能夠判斷收到的密文是否被篡改過。 這裡你可能會問:那如果C同時偽造訊息認證碼呢? 這個就得看MAC和加密是如何配合的了,詳情可以檢視認證加密中的Approaches to Authenticated Encryption章節。
在TLS握手和資料傳輸的不同階段會採用相應的演算法:
- 服務端身份驗證:數字簽名(RSA、ECDSA)
- 金鑰交換:RSA/金鑰交換演算法(ECDH)
- 加密/解密:流加密(RC4)和分組加密(3DES/AES/AESGCM)
- 生成訊息認證碼:SHA/AEAD
不知是否有人發現並沒有提到壓縮演算法,如果google下TLS壓縮優化相關的內容,會發現沒有,因為目前在TLS 1.2 RFC中,關於壓縮方法的結構定義為
enum { null(0), (255) } CompressionMethod;
,即只有null方法(不進行壓縮)。目前存在對TLS壓縮的攻擊:http://www.freebuf.com/articles/web/5636.html,可能是基於此原因,TLS壓縮目前只是個概念性的東西,沒有被真正應用起來。
如何選擇演算法——安全性
通常加密演算法的安全性依賴於金鑰的長度,且不同加密演算法,即使金鑰長度相同,但提供的安全性也可能是不同的,相關資料:key size。所以並沒有一個標準的歸一化方法去衡量所有的加密演算法,但是有來自世界上各個組織/機構對不同型別演算法安全性的評估,可以看下這個網站:https://www.keylength.com/
執行openssl ciphers -v 'ALL' | wc -l
會發現有100+個密碼套件(不同openssl版本提供的密碼套件有點差異),然而,實際只會使用到其中一部分,因為openssl提供的不少演算法是不安全的,需要排除掉。
執行openssl ciphers -v 'HIGH MEDIUM !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !CAMELLIA !IDEA !SEED !RC4' | wc -l
,發現只剩下50+個密碼套件。
篩選後剩下的密碼套件還是挺多的,一個個做效能測試的話,會GG的= =。其實可以根據需要支援的客戶端,再篩選出主流的密碼套件。網址:https://www.ssllabs.com/ssltest/clients.html,提供了絕大部分客戶端對TLS的支援情況,點選相應的User agent可以檢視到其支援的密碼套件,並且各套件的安全性也被標註出來了。
網址:https://www.ssllabs.com/ssltest/,可以用於測試伺服器的SSL配置情況,並會給出得分,如下圖google的得分為A:
如何選擇演算法——效能
以下效能測試都是選取主流的演算法進行。
數字簽名:ECDSA vs RSA
需要先分別生成採用ECDSA和RSA的簽名證書。
生成ECDSA自簽名的證書:
openssl ecparam -name prime256v1 -genkey -out ec_key.pem openssl req -new -x509 -key ec_key.pem -out cert.pem -days 365
-param_enc引數使用預設的named_curve就可以了,如果使用explicit,會發現生成的證書nginx能配置成功,但客戶端連線時會出現handshake error。
生成RSA簽名的證書:
openssl req -newkey rsa:2048 -nodes -keyout rsa_key.pem -x509 -days 365 -out cert.pem
執行openssl speed rsa2048 ecdsap256
測試下:
sign verify sign/s verify/s rsa 2048 bits 0.000834s 0.000024s 1198.9 41031.9 sign verify sign/s verify/s 256 bit ecdsa (nistp256) 0.0000s 0.0001s 21302.5 9728.5
可以看到簽名效能ECDSA > RSA,而驗證效能RSA > ECDSA。
測試環境:
- 服務端:1臺虛擬機器CentOS 4核 openresty 2個worker
- 客戶端:4臺虛擬機器CentOS 4/2/2/2核(手頭只有這些虛擬機器= =), 用shell指令碼模擬併發的
ab -c 800 -n 800
(併發的ab例項數=2*CPU_NUM),使用time命令獲取消耗的時間 - 測試頁面562位元組,目標是測試數字簽名的效能,所以頁面小點,避免加密/解密、資料傳輸佔用太多時間
多臺客戶端如何同時啟動?ctrl+tab,命令+回車……
為什麼不用jmeter?我用了1Master3Slave的jmeter分散式壓測發現,jmeter對於在該場景(CPU bound)下的效能測試不行,服務端壓力上不去
在相同的請求量下,RSA簽名會使服務端CPU佔用更高,所以這次測試需要在兩種簽名的壓測下,服務端CPU都保持在90%以上(不然的話,對ECDSA就不公平了)。
為何openresty是2個worker?因為開4個的話,ECDSA的壓測沒法使openresty4個worker的CPU消耗達到90%
ECDHE-ECDSA-AES128-GCM-SHA256,服務端CPU佔比90%,結果:
客戶端(CPU核數標識) | 4 | 2 | 2 | 2 |
第一次 | 11.988 | 17.334 | 9.161 | 7.748 |
第二次 | 12.524 | 13.750 | 12.129 | 7.582 |
第三次 | 11.836 | 17.991 | 9.195 | 10.023 |
第四次 | 11.617 | 7.081 | 9.168 | 8.919 |
ECDHE-RSA-AES128-GCM-SHA256,服務端CPU佔比100%,結果:
客戶端(CPU核數標識) | 4 | 2 | 2 | 2 |
第一次 | 12.704 | 21.088 | 18.232 | 6.134 |
第二次 | 13.355 | 21.071 | 26.990 | 6.102 |
第三次 | 14.638 | 16.009 | 11.669 | 6.071 |
第四次 | 13.913 | 21.061 | 21.271 | 5.108 |
從表格中的資料可以看出ECDSA的效能要比RSA好點,這裡ECDSA的測試尚未壓榨完服務端呢。從openssl speed的結果也可以看出ECDSA的簽名效能是要遠超過RSA的,而且簽名是在服務端做的,所以面對海量的客戶端,服務端應該選擇使用ECDSA。
金鑰交換:RSA vs ECDHE
測試環境同上,但只使用了4/2核兩臺客戶端機器發請求。證書使用的是生成的RSA證書,ECDSA證書能用到的金鑰交換演算法只能是ECDHE。
AES256-GCM-SHA384,服務端CPU佔比100%,結果:
客戶端(CPU核數標識) | 4 | 2 |
第一次 | 12.144 | 15.737 |
第二次 | 12.133 | 15.452 |
第三次 | 11.902 | 16.145 |
第四次 | 11.614 | 16.133 |
ECDHE-RSA-AES256-GCM-SHA384,服務端CPU佔比100%,結果:
客戶端(CPU核數標識) | 4 | 2 |
第一次 | 11.950 | 16.213 |
第二次 | 12.488 | 16.666 |
第三次 | 12.167 | 16.378 |
第四次 | 13.784 | 16.484 |
從表格中的資料可以看出ECDHE與RSA的效能差不多。ECDHE比RSA要多了一次端到端的傳輸,還會用到RSA對DH引數進行簽名和驗證;而RSA金鑰交換則會使用到RSA的加密/解密,具體可看如下CloudFlare的兩張圖,圖片來自Keyless SSL: The Nitty Gritty Technical Details:
ECDHE支援前向保密(Forward Secrecy),簡單理解:中間人可以儲存下來客戶端和服務端之間的所有通訊資料,如果使用RSA握手,那麼未來某一天,中間人如果獲取到了服務端的私鑰,就可以解密所有之前採集的通訊資料了;如果採用ECDHE握手的話,就可以避免這個問題。而且使用ECDHE握手的話,還有可能開啟TLS false start的特性(下文中會提到)。
RSA握手:
ECDHE握手:
所以金鑰交換演算法ECDHE會更好些。
對稱加密:AES256-GCM vs AES256 vs AES128-GCM vs 3DES
測試環境同上,但只使用了4核一臺客戶端機器發請求,ab引數為ab -n 2000 -c 10
,ab例項4個,測試頁面153K。因為是要壓測對應用層資料的加密解密效能,所以連線數少,但每個連線的請求數多。
ECDHE-RSA-AES256-GCM-SHA384,服務端CPU佔比94%,結果:
客戶端(CPU核數標識) | 4 |
第一次 | 17.972 |
第二次 | 18.863 |
第三次 | 18.761 |
第四次 | 19.345 |
ECDHE-RSA-AES256-SHA384,服務端CPU佔比98%,結果:
客戶端(CPU核數標識) | 4 |
第一次 | 20.490 |
第二次 | 19.575 |
第三次 | 19.725 |
第四次 | 20.262 |
ECDHE-RSA-AES128-GCM-SHA256,服務端CPU佔比92%,結果:
客戶端(CPU核數標識) | 4 |
第一次 | 17.886 |
第二次 | 18.449 |
第三次 | 17.897 |
第四次 | 18.371 |
DES-CBC3-SHA,服務端CPU佔比100%,結果(太慢了,就測了兩個=。=):
客戶端(CPU核數標識) | 4 |
第一次 | 52.262 |
第二次 | 51.476 |
從表格中的資料可以看出AES128GCM > AES256GCM > AES256 > 3DES。
訊息認證碼:SHA256 vs SHA1 vs AEAD
測試環境同上。
AES256-SHA256,服務端CPU佔比100%,結果:
客戶端(CPU核數標識) | 4 |
第一次 | 18.544 |
第二次 | 18.309 |
第三次 | 18.594 |
第四次 | 18.670 |
AES256-SHA,服務端CPU佔比98%,結果:
客戶端(CPU核數標識) | 4 |
第一次 | 15.418 |
第二次 | 15.071 |
第三次 | 16.614 |
第四次 | 16.146 |
AES256-GCM-SHA384,服務端CPU佔比95%,結果:
客戶端(CPU核數標識) | 4 |
第一次 | 14.443 |
第二次 | 15.669 |
第三次 | 15.880 |
第四次 | 15.960 |
從結果中可以看出AES256-GCM-SHA384 > AES256-SHA > AES256-SHA256。
會話恢復
Session Cache
客戶端希望恢復先前的session,或者複製一個存在的session,可以在ClientHello中帶上Session ID,如果服務端能夠在它的Session Cache中找到相應的Session ID的session-state(儲存協商好的密碼套件等資訊),並且願意使用該Session ID重建連線,那麼服務端會傳送一個帶有相同Session ID的ServerHello。
目前Nginx 只支援單機Session Cache,Openresty 支援分散式Session Cache,但處於實驗階段。
Session Ticket
Session Cache需要服務端快取Session相關的資訊,對服務端存在存取壓力,而且還有分散式Session Cache問題。 對於支援Session Ticket的客戶端,服務端可以通過某種機制將session-state加密後作為ticket發給客戶端。客戶端憑藉該ticket就可以恢復先前的會話了。
類似於HTTP中用Json Web TOken作為cookie-session的另一種選擇。
OCSP(線上證書狀態協議) stapling
當客戶端在握手環節接受到服務端的證書時,除了對證書進行簽名驗證,還需要知道證書是否被吊銷了,那麼需要向證書中指定的OCSP url傳送OCSP查詢請求。
對於同一份服務端證書,如果每個客戶端都自己去查詢一次證書狀態就浪費了。所以,OCSP stapling就是為了解決這一問題,由服務端查詢到證書狀態(通常會快取一段時間),並返回給客戶端(客戶端會在本地校驗這個證書狀態是否真實)。
在nginx的配置中,可以選擇性的配置是否對OCSP response做校驗,防止將非法的證書狀態傳送給客戶端。如果設定了校驗,ssl_trusted_certificate引數需要為包含所有中間證書+根證書
的檔案。
如下圖是對nginx請求OCSP Server的抓包,可以看到發了個http的ocsp請求:
下圖是對nginx在傳送證書給客戶端時,帶上的證書狀態的抓包:
TLS緩衝區調優
nginx預設的ssl_buffer_size是16K(TLS Record Layer最大的分片),即一個TLS Record的大小,如果HTTP的資料是160K,那麼就會被拆分為10個TLS Record(每個TLS Record會被TCP層拆分為多個TCP包傳輸)傳送給客戶端。
如果TLS Record Size過大的話,拆分的TCP包也會較多,傳輸時,如果出現TCP丟包,整個TLS Record到達客戶端的時間就會加長,客戶端必須等待完整的TLS Record收到才能進行解密。
如果TLS Record Size小一些的話,TCP丟包影響的TLS Record佔比就會小很多,到達客戶端的TLS Record就會多些,客戶端乾等著的時間就相對少了。但是,TLS Record Head的負載就增加了,可能還會降低連線的吞吐量。
假設ssl_buffer_size設定為1460byte:
可以看下這篇文章關於:Nginx TLS 首位元組的優化
通常,在TCP慢啟動的過程中,TLS Record Size小點好,因為這個時候TCP連線的擁塞視窗cwnd較小,TCP連線吞吐量也小。而在TCP連線結束慢啟動之後,TLS Record Size就可以增大一些了,因為這個時候吞吐量上來了。所以更希望能夠動態的調整nginx中ssl_buffer_size的大小,目前官方nginx還不支援,不過cloudflare為nginx打了個patch,以支援動態的調整TLS Record Size:Optimizing TLS over TCP to reduce latency
TLS False Start
某一端在傳送 Change Cipher Spec、Finished 之後,可以立即傳送應用資料,無需等待另一端的 Change Cipher Spec、Finished 。這樣,應用資料的傳送實際上並未等到握手全部完成,從而節省出一個RTT時間。
完整握手時,Client Side False Start:
簡短握手時,Server Side False Start:
可以看下這篇文章:TLS False Start究竟是如何加速網站的 和 Transport Layer Security (TLS) False Start
RFC7918中並沒有對Server Side False Start進行定義(其之前的草案中就有提到,draft-bmoeller-tls-falsestart-00/01),文中的說明:However, if the server sends application data first, the abbreviated handshake adds two round-trip times, and this could be reduced to just one added round-trip time by doing a server-side False Start. There is little need for this in practice, so this document does not consider server-side False Starts further.
可能是在之前的HTTP 1場景下,對Server Side False Start的需求並不強烈,或者說實踐不多(當然其他應用層協議可能會有,例如websocket)。
Client Side False Start需要的條件:
- 客戶端和服務端都需要支援NPN/ALPN(瀏覽器要求)
- 需要採用支援前向保密的密碼套件,即使用ECDHE進行金鑰交換(RFC7918中有規定)
其他優化
- TCP優化,畢竟SSL資料也是基於TCP進行傳輸的
- 證書優化,採用ECDSA證書、伺服器傳送給客戶端的證書鏈包含所有中間證書
- 硬體配置優化,例如使用SSL加速器
總結
本文是個人近段時間學習到的關於HTTPS效能優化的總結,推薦閱讀HTTPS權威指南和High Performance Browser Networking以瞭解更多內容。
推薦的密碼套件列表:
openssl ciphers -v 'ECDHE+ECDSA ECDHE AESGCM AES HIGH MEDIUM !kDH !kECDH !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !CAMELLIA !IDEA !SEED !RC4 !3DES'
其他額外的密碼套件,比如需要支援IE6,可以放在密碼套件列表末尾。
自己寫了個go程式用於檢測密碼套件列表支援/不支援的客戶端:sslciphersuitescheck
相關文章
- oracle 學習筆記---效能優化學習(1)Oracle筆記優化
- ORACLE學習筆記--效能優化FAQ。Oracle筆記優化
- oracle 學習筆記---效能優化(1)Oracle筆記優化
- oracle 學習筆記---效能優化(2)Oracle筆記優化
- oracle 學習筆記---效能優化(3)Oracle筆記優化
- oracle 學習筆記---效能優化(4)Oracle筆記優化
- oracle 學習筆記---效能優化(5)Oracle筆記優化
- oracle 學習筆記---效能優化(6)Oracle筆記優化
- oracle 學習筆記---效能優化(7)Oracle筆記優化
- Oracle效能優化視訊學習筆記-效能優化概念(一)Oracle優化筆記
- Oracle效能優化視訊學習筆記-效能優化概念(二)Oracle優化筆記
- Nginx效能優化(學習筆記二十五)Nginx優化筆記
- iOS學習筆記-TableView效能優化篇1iOS筆記View優化
- iOS學習筆記-TableView效能優化篇2iOS筆記View優化
- 斜率優化學習筆記優化筆記
- mysql優化學習筆記MySql優化筆記
- 強烈推薦:ORACLE學習筆記--效能優化(ZT)Oracle筆記優化
- Oracle效能優化視訊學習筆記-診斷和調優工具Oracle優化筆記
- Mysql 優化(學習筆記二十)MySql優化筆記
- Web 效能優化筆記Web優化筆記
- ORACLE效能優化筆記Oracle優化筆記
- Oracle效能優化視訊學習筆記-動態調整SGAOracle優化筆記
- MySQL優化學習筆記之索引MySql優化筆記索引
- MySQL優化學習筆記之explainMySql優化筆記AI
- Android卡頓優化學習筆記Android優化筆記
- ORACLE學習筆記--效能最佳化一Oracle筆記
- ORACLE學習筆記--效能最佳化四Oracle筆記
- ORACLE學習筆記--效能最佳化三Oracle筆記
- ORACLE學習筆記--效能最佳化二Oracle筆記
- Linux 效能優化筆記Linux優化筆記
- Android效能優化 筆記Android優化筆記
- Android效能優化---筆記Android優化筆記
- MySql 學習筆記三:常用SQL優化MySql筆記優化
- JDBC學習筆記-----jdbc效能最佳化 (轉)JDBC筆記
- PHP7效能優化筆記PHP優化筆記
- 【AIX 學習】效能優化--vmstatAI優化
- 【AIX 學習】效能優化--sarAI優化
- 【AIX 學習】效能優化--topasAI優化