Spring Cloud Gateway 2.2.3 使用說明(5)-- TLS 和 SSL

2020-08-14 19:09:36

9. TLS 和 SSL

閘道器可以通過常規的 Spring server configuration 來偵聽HTTPS上的請求。例子:

Example 61. application.yml

server:
  ssl:
    enabled: true
    key-alias: scg
    key-store-password: scg1234
    key-store: classpath:scg-keystore.p12
    key-store-type: PKCS12

閘道器路由可以路由到HTTP和HTTPS後端。如果路由到HTTPS後端,則可以將閘道器設定爲信任所有具有證書的下遊服務:

Example 62. application.yml

spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          useInsecureTrustManager: true

不建議在生產環境使用不安全的信任管理器。對於生產部署,可以使用一組已知證書設定閘道器,這些證書可以通過以下方式進行設定:

Example 63. application.yml

spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          trustedX509Certificates:
          - cert1.pem
          - cert2.pem

如果Spring Cloud Gateway未設定受信任證書,則使用預設信任庫(可以使用系統屬性javax.net.ssl.trustStore覆蓋)。

9.1. TLS 握手

閘道器維護一個用於路由到後端的client池。當通過HTTPS通訊時,用戶端啓動一個TLS握手,其中可能會有很多超時。這些超時可以這樣設定(顯示預設值):

Example 64. application.yml

spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          handshake-timeout-millis: 10000
          close-notify-flush-timeout-millis: 3000
          close-notify-read-timeout-millis: 0

9.1.1. 握手與金鑰協商過程

基於RSA握手和金鑰交換的用戶端驗證伺服器爲範例詳解TLS/SSL握手過程
在这里插入图片描述
1.client_hello

用戶端發起請求,以明文傳輸請求資訊,包含版本資訊,加密套件候選列表,壓縮演算法候選列表,亂數,擴充套件欄位等資訊,相關資訊如下:

• 支援的最高TSL協定版本version,從低到高依次 SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2,當前基本不再使用低於 TLSv1 的版本;
• 用戶端支援的加密套件 cipher suites 列表, 每個加密套件對應前面 TLS 原理中的四個功能的組合:認證演算法 Au (身份驗證)、金鑰交換演算法 KeyExchange(金鑰協商)、對稱加密演算法 Enc (資訊加密)和資訊摘要 Mac(完整性校驗);
• 支援的壓縮演算法 compression methods 列表,用於後續的資訊壓縮傳輸;
• 亂數 random_C,用於後續的金鑰的生成;
• 擴充套件欄位 extensions,支援協定與演算法的相關參數以及其它輔助資訊等,常見的 SNI 就屬於擴充套件欄位,後續單獨討論該欄位作用。

2.server_hello+server_certificate+sever_hello_done

• server_hello, 伺服器端返回協商的資訊結果,包括選擇使用的協定版本 version,選擇的加密套件 cipher suite,選擇的壓縮演算法 compression method、亂數 random_S 等,其中亂數用於後續的金鑰協商;
• server_certificates, 伺服器端設定對應的證書鏈,用於身份驗證與金鑰交換;
• server_hello_done,通知用戶端 server_hello 資訊發送結束;
3.證書校驗
用戶端驗證證書的合法性,如果驗證通過纔會進行後續通訊,否則根據錯誤情況不同做出提示和操作,合法性驗證包括如下:
• [證書鏈]的可信性 trusted certificate path;
• 證書是否吊銷 revocation,有兩類方式離線 CRL 與線上 OCSP,不同的用戶端行爲會不同;
• 有效期 expiry date,證書是否在有效時間範圍;
• 域名 domain,覈查證書域名是否與當前的存取域名匹配,匹配規則後續分析;
4.client_key_exchange+change_cipher_spec+encrypted_handshake_message

  • client_key_exchange,合法性驗證通過之後,用戶端計算產生亂數字 Pre-master,並用證書公鑰加密,發送給伺服器;
  • 此時用戶端已經獲取全部的計算協商金鑰需要的資訊:兩個明文亂數 random_C 和 random_S 與自己計算產生的 Pre-master,計算得到協商金鑰;
    enc_key=Fuc(random_C, random_S, Pre-Master)
  • change_cipher_spec,用戶端通知伺服器後續的通訊都採用協商的通訊金鑰和加密演算法進行加密通訊;
  • encrypted_handshake_message,結合之前所有通訊參數的 hash 值與其它相關資訊生成一段數據,採用協商金鑰 session secret 與演算法進行加密,然後發送給伺服器用於數據與握手驗證;

5.change_cipher_spec+encrypted_handshake_message

  • 伺服器用私鑰解密加密的 Pre-master 數據,基於之前交換的兩個明文亂數 random_C 和 random_S,計算得到協商金鑰:enc_key=Fuc(random_C, random_S, Pre-Master);
  • 計算之前所有接收資訊的 hash 值,然後解密用戶端發送的 encrypted_handshake_message,驗證數據和金鑰正確性;
  • change_cipher_spec, 驗證通過之後,伺服器同樣發送 change_cipher_spec 以告知用戶端後續的通訊都採用協商的金鑰與演算法進行加密通訊;
  • encrypted_handshake_message, 伺服器也結合所有當前的通訊參數資訊生成一段數據並採用協商金鑰 session secret 與演算法加密併發送到用戶端;

6.握手結束
用戶端計算所有接收資訊的 hash 值,並採用協商金鑰解密 encrypted_handshake_message,驗證伺服器發送的數據和金鑰,驗證通過則握手完成;
7.加密通訊
開始使用協商金鑰與演算法進行加密通訊。
注意:

  • 伺服器也可以要求驗證用戶端,即雙向認證,可以在過程2要發送 client_certificate_request 資訊,用戶端在過程4中先發送 client_certificate與certificate_verify_message 資訊,證書的驗證方式基本相同,certificate_verify_message 是採用client的私鑰加密的一段基於已經協商的通訊資訊得到數據,伺服器可以採用對應的公鑰解密並驗證;
  • 根據使用的金鑰交換演算法的不同,如 ECC 等,協商細節略有不同,總體相似;
  • sever key exchange 的作用是 server certificate 沒有攜帶足夠的資訊時,發送給用戶端以計算 pre-master,如基於 DH 的證書,公鑰不被證書中包含,需要單獨發送;
  • change cipher spec 實際可用於通知對端改版當前使用的加密通訊方式,當前沒有深入解析;
  • alter message 用於指明在握手或通訊過程中的狀態改變或錯誤資訊,一般告警資訊觸發條件是連線關閉,收到不合法的資訊,資訊解密失敗,使用者取消操作等,收到告警資訊之後,通訊會被斷開或者由接收方決定是否斷開連線。

9.1.2 對談快取握手過程

爲了加快建立握手的速度,減少協定帶來的效能降低和資源消耗(具體分析在後文),TLS 協定有兩類對談快取機制 機製:對談標識 session ID 與對談記錄 session ticket。

session ID 由伺服器端支援,協定中的標準欄位,因此基本所有伺服器都支援,伺服器端儲存對談ID以及協商的通訊資訊,Nginx 中1M 記憶體約可以儲存4000個 session ID 機器相關資訊,佔用伺服器資源較多;

session ticket 需要伺服器和用戶端都支援,屬於一個擴充套件欄位,支援範圍約60%(無可靠統計與來源),將協商的通訊資訊加密之後發送給用戶端儲存,金鑰只有伺服器知道,佔用伺服器資源很少。

二者對比,主要是儲存協商資訊的位置與方式不同,類似與 http 中的 session 與 cookie。

二者都存在的情況下,(nginx 實現)優先使用 session_ticket。

握手過程如下圖:
在这里插入图片描述注意:雖然握手過程有1.5個來回,但是最後用戶端向伺服器發送的第一條應用數據不需要等待伺服器返回的資訊,因此握手延時是1*RTT。

  1. 對談標識 session ID
    (a) 如果用戶端和伺服器之間曾經建立了連線,伺服器會在握手成功後返回 session ID,並儲存對應的通訊參數在伺服器中;
    (b) 如果用戶端再次需要和該伺服器建立連線,則在 client_hello 中 session ID 中攜帶記錄的資訊,發送給伺服器;
    ( c) 伺服器根據收到的 session ID 檢索快取記錄,如果沒有檢索到貨快取過期,則按照正常的握手過程進行;
    (d) 如果檢索到對應的快取記錄,則返回 change_cipher_spec 與 encrypted_handshake_message 資訊,兩個資訊作用類似,encrypted_handshake_message 是到當前的通訊參數與 master_secret的hash 值;
    (e) 如果用戶端能夠驗證通過伺服器加密數據,則用戶端同樣發送 change_cipher_spec 與 encrypted_handshake_message 資訊;
    (f) 伺服器驗證數據通過,則握手建立成功,開始進行正常的加密數據通訊。

  2. 對談記錄 session ticket
    (a) 如果用戶端和伺服器之間曾經建立了連線,伺服器會在 new_session_ticket 數據中攜帶加密的 session_ticket 資訊,用戶端儲存;
    (b) 如果用戶端再次需要和該伺服器建立連線,則在 client_hello 中擴充套件欄位 session_ticket 中攜帶加密資訊,一起發送給伺服器;
    © 伺服器解密 sesssion_ticket 數據,如果能夠解密失敗,則按照正常的握手過程進行;
    (d) 如果解密成功,則返回 change_cipher_spec 與 encrypted_handshake_message 資訊,兩個資訊作用與 session ID 中類似;
    (e)如果用戶端能夠驗證通過伺服器加密數據,則用戶端同樣發送 change_cipher_spec與encrypted_handshake_message 資訊;
    (f) 伺服器驗證數據通過,則握手建立成功,開始進行正常的加密數據通訊。

9.1.3 重建連線

重建連線 renegotiation 即放棄正在使用的 TLS 連線,從新進行身份認證和金鑰協商的過程,特點是不需要斷開當前的數據傳輸就可以重新身份認證、更新金鑰或演算法,因此伺服器端儲存和快取的資訊都可以保持。用戶端和伺服器都能夠發起重建連線的過程,當前 windows 2000 & XP 與 SSL 2.0不支援。

  1. 伺服器重建連線
    在这里插入图片描述 伺服器端重建連線一般情況是用戶端存取受保護的數據時發生。基本過程如下:

    (a) 用戶端和伺服器之間建立了有效 TLS 連線並通訊;
    (b) 用戶端存取受保護的資訊;
    (c ) 伺服器端返回 hello_request 資訊;
    (d) 用戶端收到 hello_request 資訊之後發送 client_hello 資訊,開始重新建立連線。

  2. 用戶端重建連線

在这里插入图片描述用戶端重建連線一般是爲了更新通訊金鑰。
(a) 用戶端和伺服器之間建立了有效 TLS 連線並通訊;
(b) 用戶端需要更新金鑰,主動發出 client_hello 資訊;
(c ) 伺服器端收到 client_hello 資訊之後無法立即識別出該資訊非應用數據,因此會提交給下一步處理,處理完之後會返回通知該資訊爲要求重建連線;
(d) 在確定重建連線之前,伺服器不會立即停止向用戶端發送數據,可能恰好同時或有快取數據需要發送給用戶端,但是用戶端不會再發送任何資訊給伺服器;
(e) 伺服器識別出重建連線請求之後,發送 server_hello 資訊至用戶端;
(f) 用戶端也同樣無法立即判斷出該資訊非應用數據,同樣提交給下一步處理,處理之後會返回通知該資訊爲要求重建連線;
(g) 用戶端和伺服器開始新的重建連線的過程。

9.1.4. 金鑰計算

上節提到了兩個明文傳輸的亂數 random_C 和 random_S 與通過加密在伺服器和用戶端之間交換的 Pre-master,三個參數作爲金鑰協商的基礎。本節討論說明金鑰協商的基本計算過程以及通訊過程中的金鑰使用。

  1. 計算 Key
    涉及參數 random client 和 random server, Pre-master, Master secret, key material, 計算金鑰時,伺服器和用戶端都具有這些基本資訊,交換方式在上節中有說明,計算流程如下:

在这里插入图片描述 (a) 用戶端採用 RSA 或 Diffie-Hellman 等加密演算法生成 Pre-master;
(b) Pre-master 結合 random client 和 random server 兩個亂數通過 PseudoRandomFunction(PRF)計算得到 Master secret;
© Master secret 結合 random client 和 random server 兩個亂數通過迭代計算得到 Key material;

以下爲一些重要的記錄,可以解決部分愛深入研究朋友的疑惑,copy的材料,分享給大家:
(a) PreMaster secret 前兩個位元組是 TLS 的版本號,這是一個比較重要的用來覈對握手數據的版本號,因爲在 Client Hello 階段,用戶端會發送一份加密套件列表和當前支援的 SSL/TLS 的版本號給伺服器端,而且是使用明文傳送的,如果握手的數據包被破解之後,攻擊者很有可能串改數據包,選擇一個安全性較低的加密套件和版本給伺服器端,從而對數據進行破解。所以,伺服器端需要對密文中解密出來對的 PreMaster 版本號跟之前 Client Hello 階段的版本號進行對比,如果版本號變低,則說明被串改,則立即停止發送任何訊息。(copy)
(b) 不管是用戶端還是伺服器,都需要亂數,這樣生成的金鑰纔不會每次都一樣。由於 SSL 協定中證書是靜態的,因此十分有必要引入一種隨機因素來保證協商出來的金鑰的隨機性。
對於 RSA 金鑰交換演算法來說,pre-master-key 本身就是一個亂數,再加上 hello 訊息中的隨機,三個亂數通過一個金鑰導出器最終導出一個對稱金鑰。
pre master 的存在在於 SSL 協定不信任每個主機都能產生完全隨機的亂數,如果亂數不隨機,那麼 pre master secret 就有可能被猜出來,那麼僅適用 pre master secret 作爲金鑰就不合適了,因此必須引入新的隨機因素,那麼用戶端和伺服器加上 pre master secret 三個亂數一同生成的金鑰就不容易被猜出了,一個僞隨機可能完全不隨機,可是三個僞隨機就十分接近隨機了,每增加一個自由度,隨機性增加的可不是一。

  1. 金鑰使用
    Key 經過12輪迭代計算會獲取到12個 hash 值,分組成爲6個元素,列表如下:
    在这里插入图片描述
    (a) mac key、encryption key 和 IV 是一組加密元素,分別被用戶端和伺服器使用,但是這兩組元素都被兩邊同時獲取;
    (b) 用戶端使用 client 組元素加密數據,伺服器使用 client 元素解密;伺服器使用 server 元素加密,client 使用 server 元素解密;
    (c ) 雙向通訊的不同方向使用的金鑰不同,破解通訊至少需要破解兩次;
    (d) encryption key 用於對稱加密數據;
    (e) IV 作爲很多加密演算法的初始化向量使用,具體可以研究對稱加密演算法;
    (f) Mac key 用於數據的完整性校驗;
  2. 數據加密通訊過程
    (a) 對應用層數據進行分片成合適的 block;
    (b) 爲分片數據編號,防止重放攻擊;
    (c ) 使用協商的壓縮演算法壓縮數據;
    (d) 計算 MAC 值和壓縮數據組成傳輸數據;
    (e) 使用 client encryption key 加密數據,發送給伺服器 server;
    (f) server 收到數據之後使用 client encrytion key 解密,校驗數據,解壓縮數據,重新組裝。
    注:MAC值的計算包括兩個 Hash 值:client Mac key 和 Hash (編號、包型別、長度、壓縮數據)。

9.1.5 抓包分析

關於抓包不再詳細分析,按照前面的分析,基本的情況都能夠匹配,根據平常定位問題的過程,個人提些認爲需要注意的地方:
(1).抓包 HTTP 通訊,能夠清晰的看到通訊的頭部和資訊的明文,但是 HTTPS 是加密通訊,無法看到 HTTP 協定的相關頭部和數據的明文資訊,
(2).抓包 HTTPS 通訊主要包括三個過程:TCP 建立連線、TLS 握手、TLS 加密通訊,主要分析 HTTPS 通訊的握手建立和狀態等資訊。
(3).client_hello
根據 version 資訊能夠知道用戶端支援的最高的協定版本號,如果是 SSL 3.0 或 TLS 1.0 等低版本協定,非常注意可能因爲版本低引起一些握手失敗的情況;
根據 extension 欄位中的 server_name 欄位判斷是否支援SNI,存在則支援,否則不支援,對於定位握手失敗或證書返回錯誤非常有用;
對談標識 session ID 是標準協定部分,如果沒有建立過連線則對應值爲空,不爲空則說明之前建立過對應的連線並快取;
對談記錄 session ticke t是擴充套件協定部分,存在該欄位說明協定支援 sesssion ticket,否則不支援,存在且值爲空,說明之前未建立並快取連線,存在且值不爲空,說明有快取連線。
(4).server_hello
根據 TLS version 欄位能夠推測出伺服器支援的協定的最高版本,版本不同可能造成握手失敗;
基於 cipher_suite 資訊判斷出伺服器優先支援的加密協定;
(5).ceritficate
伺服器設定並返回的證書鏈,根據證書資訊並於伺服器組態檔對比,判斷請求與期望是否一致,如果不一致,是否返回的預設證書。
(6).alert
告警資訊 alert 會說明建立連線失敗的原因即告警型別,對於定位問題非常重要。

參考:
https://www.cnblogs.com/barrywxx/p/8570715.html