阿里云环境中TLS/SSL握手失败的场景分析
TLS/SSL握手是一個相對復雜的過程,在阿里云環境中結合產品,安全等特性,可能會讓TLS/SSL握手過程的不定性更多。本文來總結下各種握手失敗的場景。
一次TLS/SSL握手的過程
本文不詳細介紹TLS/SSL基礎知識,相關介紹可以參考文章。下面3張圖描述了3種TLS/SSL握手的全過程。
服務器驗證的完全握手 (Full Handshake with Mutual Authentication)
 這種是互聯網大部分HTTPS流量使用的驗證模式。證書在服務器上,客戶端通過證書來驗證服務器是否可靠。
雙向驗證的完全握手 (Full Handshake with Server Authentication)
 這種是對客戶端安全性有要求的驗證模式。除了客戶端要驗證服務器外,服務器對客戶端也需要進行驗證,所以需要雙向驗證。和上面的步驟相比,多了客戶端向服務器傳輸證書的過程。
簡單握手 (Abbreviated Handshake)
 完全握手需要2個RTT并交互很多消息,在會話復用的場景下,可以讓握手簡化到1個RTT完成。過程如下:
常規TLS/SSL握手失敗
TLS/SSL版本不匹配
自從TLS 1.2版本在2008年發布以來,絕大部分HTTPS流量都跑在TLS 1.2上。服務器處于安全性考慮通常也只支持較高版本TLS,比如TLS1.0及以上。但是仍然有一些版本比較舊的操作系統和瀏覽器存在,如果這些客戶端用低版本TLS/SSL向服務器發起握手,會因為服務器不支持而直接失敗。
比如淘寶網只支持TLS 1.0及以上版本,用openssl發起SSL 3版本的握手,就會出現handshake failure。
# openssl s_client -connect www.taobao.com:443 -ssl3 -msg CONNECTED(00000003) >>> ??? [length 0005]16 03 00 00 8f >>> SSL 3.0 Handshake [length 008f], ClientHello01 00 00 8b 03 00 2a a0 d3 c5 10 b0 0a c0 0b eafc e7 49 8f d1 66 cd 2a 51 c1 ab f4 ab b7 63 e1a7 3e e0 d7 14 9b 00 00 64 c0 14 c0 0a 00 39 0038 00 37 00 36 00 88 00 87 00 86 00 85 c0 0f c005 00 35 00 84 c0 13 c0 09 00 33 00 32 00 31 0030 00 9a 00 99 00 98 00 97 00 45 00 44 00 43 0042 c0 0e c0 04 00 2f 00 96 00 41 c0 12 c0 08 0016 00 13 00 10 00 0d c0 0d c0 03 00 0a 00 07 c011 c0 07 c0 0c c0 02 00 05 00 04 00 ff 01 00 <<< ??? [length 0005]15 03 00 00 02 <<< SSL 3.0 Alert [length 0002], fatal handshake_failure02 28 140191222585232:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:s3_pkt.c:1493:SSL alert number 40 140191222585232:error:1409E0E5:SSL routines:ssl3_write_bytes:ssl handshake failure:s3_pkt.c:659: --- no peer certificate available --- No client certificate CA names sentTLS/SSL cipher suite不匹配
在握手的前兩個ClientHello和ServerHello包中有一個重要的任務就是協商cipher。客戶端在ClientHello中會帶上所有支持的cipher suite, 服務器在收到ClientHello中的cipher suite后,會和自己支持的cipher suite一一匹配,如果沒有可以匹配的就會握手失敗。
服務器出于安全性考慮通常只會支持安全性較高的cipher,所以當客戶端發過去的cipher suite安全性都比較低時會造成握手失敗。
例如用openssl向淘寶網發起握手,客戶端的ClientHello中只有一個安全性較低的DHE-RSA-AES128-SHA256 cipher,會出現handshake failure。
# openssl s_client -connect www.taobao.com:443 -cipher DHE-RSA-AES128-SHA256 -msg CONNECTED(00000003) >>> TLS 1.2 [length 0005]16 03 01 00 5e >>> TLS 1.2 Handshake [length 005e], ClientHello01 00 00 5a 03 03 4a d3 f5 53 f0 f3 e2 8f a8 a34a 26 81 91 84 fb fd cf 80 13 21 c6 42 d3 c4 2ba7 70 de 4c e0 48 00 00 04 00 67 00 ff 01 00 002d 00 23 00 00 00 0d 00 20 00 1e 06 01 06 02 0603 05 01 05 02 05 03 04 01 04 02 04 03 03 01 0302 03 03 02 01 02 02 02 03 00 0f 00 01 01 <<< TLS 1.2 [length 0005]15 03 03 00 02 <<< TLS 1.2 Alert [length 0002], fatal handshake_failure02 28 139737777813392:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:769: --- no peer certificate available --- No client certificate CA names sentTLS/SSL握手Warning
在握手過程中,客戶端對服務器證書會做驗證,驗證不過時會出現Warning。瀏覽器可以選擇忽略,用curl也可以使用-k參數來忽略。嚴格來說并不算Failure,這里歸類成Warning,不做詳細討論。例如如下幾種比較常見的情況:
- 訪問的域名不在服務器證書的CN(Common Name)和SAN(Subject Alternative Name)中。
- 服務器證書被吊銷,導致驗證不通過。
- 由于本地系統時間不準,導致驗證證書有效期時出現誤判。
云盾導致TLS/SSL握手失敗
進入阿里云的流量會經過云盾,類似于其他安全設備,云盾會根據流量特征采取一定動作。
現象
如下是一個例子。客戶端訪問阿里云的一個公網IP地址TLS/SSL握手失敗。先來看下現象,在客戶端的抓包如下:
可以看到前面的TCP三次握手和一些數據交互(特定協議相關,正常情況在TCP三次握手后直接開始TLS/SSL握手)都沒有問題。但是開始TLS/SSL握手交互過程客戶端發出第一個報文,馬上收到一個TCP RESET。這個和上面提到的常規握手失敗很不一樣, TCP RESET報文通常是設備或者主機協議棧主動發出,符合一定場景或者有一定網絡管理含義。
根本原因
云盾根據訪問的目的域名有沒有備案做執行相關動作。云盾并沒有在TCP建連時就針對源目IP做阻斷,而是提取ClientHello中的SNI(Servername Indication)域名信息判斷是否備案而做阻斷,返回TCP RESET。
SNI是ClientHello中的一個擴展字段,帶有要訪問的目標域名,讓同一個IP上托管多個HTTPS站點的服務器知道客戶端訪問的是哪個目標域名,以便使用對應的證書進行交互。在ClientHello報文的如下位置:
客戶端證書問題導致TLS/SSL握手失敗
在雙向驗證的場景中,不僅僅客戶端要驗證服務器證書,服務器也需要驗證客戶端證書。在服務器驗證客戶端證書的過程中,由于客戶端證書的安全性較低,可能會直接產生Fatal Alert,導致握手直接中斷。
現象
如下是一個手機App訪問服務器的例子。72號報文報出了Bad Certificate的Fatal Alert,從上下文看,這里是客戶端向服務器端發送完Certificate, Client Key Exchange等消息后,服務器返回給客戶端的報錯。
在手機App中的報錯如下:
SSL handshake aborted: ssl=0x7c1bbf6e88: Failure in SSL library, usually a protocol error
 error:10000412:SSL routines:OPENSSL_internal:SSLV3_ALERT_BAD_CERTIFICATE (external/boringssl/src/ssl/tls_record.cc:592 0x7c6c627e48:0x00000001)
根本原因
在雙向認證時,openssl認為客戶端證書的安全性過低,中斷TLS/SSL握手。
無法提取SNI導致TLS/SSL握手失敗
在某些場景中,需要獲取ClientHello中的SNI字段來作為一個必要條件, 比如用NGINX stream對HTTPS流量做4層代理時。客戶端ClientHello中沒有攜帶SNI,則會造成一個通過代理握手失敗的局面。
現象
和上面個握手失敗的現象如出一轍,在客戶端發出ClientHello后,馬上被代理服務器FIN掉,唯一不同的是這里的ClientHello并沒有帶上SNI字段。
根本原因
在利用NGINX stream做正向代理時,NGXIN服務器需要獲取客戶端想要訪問的目的域名。利用ngx_stream_ssl_preread_module模塊在不解密的情況下拿到ClientHello報文中SNI才能實現代理的正常功能。詳情參考文章。
總結
上面總結了很多握手失敗的場景,一個有趣的現象是:失敗的原因可能各不相同,但是抓包的結果大部分都比較一致,即客戶端發的一個TLS/SSL握手包被服務器FIN/RST掉。對于這類問題的排查和分析,抓包分析僅僅只是一個線索,更加關鍵的是需要理解TLS/SSL握手整個過程中的細節以及當前場景中的網絡鏈路,比如鏈路中有沒有安全設備,代理,有沒有使用雙向驗證,Keyless等等。
原文鏈接
 本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的阿里云环境中TLS/SSL握手失败的场景分析的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 在Serverless Kubernet
- 下一篇: 一个优秀的可定制化Flutter相册组件
