技術分享 | MySQL : SSL 連線淺析

愛可生雲資料庫發表於2022-03-14

作者:胡呈清

愛可生 DBA 團隊成員,擅長故障分析、效能優化,個人部落格:https://www.jianshu.com/u/a95...,歡迎討論。

本文來源:原創投稿

*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。


TLS or SSL ?

SSL(Secure Socket Layer 安全套接層)是基於 HTTPS 下的一個協議加密層,最初是由網景公司(Netscape)研發,後被 IETF(The Internet Engineering Task Force - 網際網路工程任務組)標準化後寫入(RFCRequest For Comments 請求註釋),RFC 裡包含了很多網際網路技術的規範。

起初是因為HTTP在傳輸資料時使用的是明文(雖然說 POST 提交的資料時放在報體裡看不到的,但是還是可以通過抓包工具竊取到)是不安全的,為了解決這一隱患網景公司推出了SSL安全套接字協議層,SSL 是基於 HTTP 之下 TCP 之上的一個協議層,基於 HTTP 標準並對 TCP 傳輸資料時進行加密,所以 HTTPS 是 HTTP+SSL/TCP的簡稱。

由於 HTTPS 的推出受到了很多人的歡迎,在 SSL 更新到 3.0 時,IETF 對 SSL3.0 進行了標準化,並新增了少數機制(但是幾乎和 SSL3.0無差異),標準化後的 IETF 更名為 TLS1.0(Transport Layer Security 安全傳輸層協議),可以說 TLS 就是 SSL 的新版本 3.1 。

TLS(Transport Layer Security)是更為安全的升級版 SSL。但 SSL 這一術語更為常用,實際上 MySQL 使用的就是 TLS 協議,而不是 SSL 協議。

1. TLS 握手過程

想弄清楚下面這一大堆檔案的作用是什麼嗎?那就必須先了解 TLS 握手過程

├── ca-key.pem
├── ca.pem
├── client-cert.pem
├── client-key.pem
├── server-cert.pem
└── server-key.pem

1.1 TLS 握手過程

TLS 握手的過程,其實就是祕鑰交換的過程,這也是整個TLS 加密技術裡最複雜的一個環節,參考一張網上的圖如下:

1.2 金鑰演算法

對稱金鑰演算法:資料加密和解密時使用相同的金鑰。

非對稱金鑰演算法:資料加密和解密時使用不同的金鑰,一個是公開的公鑰,一個是由使用者祕密儲存的私鑰。利用公鑰(或私鑰)加密的資料只能用相應的私鑰(或公鑰)才能解密。

與非對稱金鑰演算法相比,對稱金鑰演算法具有計算速度快的優點,通常用於對大量資訊進行加密(如對所有報文加密);而非對稱金鑰演算法,一般用於數字簽名和對較少的資訊進行加密。

注意:SSL/TLS 協議就是使用對稱金鑰演算法進行資料加密,使用非對稱金鑰演算法進行”對稱金鑰“的加密。其過程為:

  1. 上圖中,Server端傳送了公鑰給客戶端,私鑰自己儲存,這是非對稱金鑰演算法中的公鑰、私鑰對;
  2. 客戶端會建立一個金鑰,這個就是對稱加密演算法中的金鑰。然後用 Server 端的公鑰對這個金鑰加密,傳送給 Server 端;
  3. Server 端收到客戶端傳送過來的加密過的金鑰,使用自己的私鑰進行解密,得到加密前的金鑰;
  4. 接下來傳輸資料則使用“對稱金鑰”進加密和解密。

這個就是利用非對稱金鑰演算法保證對稱金鑰本身的安全。

1.3 數字證書-如何保證公鑰的真實性?

如果有攻擊者偽造了 Server 端的公鑰併發了客戶端,客戶端會訪問到假網站被竊取資訊。顯然保證客戶端收到的 Server 端的公鑰是真實的,是保證整個加密連線可靠性的第一道防線。

數字證書由權威機構 CA(Certification Authority) 簽發,簽發過程為:

  1. 使用者首先產生自己的金鑰對,並將公鑰及部分個人身份資訊傳送給 CA ;
  2. CA 核實使用者身份(將執行一些必要的步驟,以確信請求確實由使用者傳送而來);
  3. CA 對使用者的所有資訊(公鑰、所有者、有效期...)進行 Hash 計算,得到一個 Hash 值,然後再使用私鑰對 Hash 值進行加密得到簽名,就得到了數字證書。該證書包含:使用者的公鑰、所有者、有效期等資訊,同時還附有CA的簽名資訊。

數字證書的驗證流程:

  1. 客戶端會使用同樣的 Hash 演算法獲取該數字證書的 Hash 值 H1;
  2. 通常瀏覽器和作業系統中整合了 CA 證書(包含 CA 公鑰、所有者),客戶端取得這個CA證書,使用其中的 CA 公鑰解密簽名,得到一個 Hash 值 H2 ;
  3. 比較 H1 和 H2,如果值相同,則數字證書可信。

上述簽發和驗證流程見下圖(參考自網路):

如果 CA 證書不在瀏覽器和作業系統的可信任區,這種 CA 證書通常被稱為自簽名 CA 證書(MySQL 自動生成的就是自簽名證書,詳見下文)。要完成數字證書的驗證,則必須事先將自簽名 CA 證書放到客戶端,並在客戶端發起連線時指定這個 CA 證書檔案;或者事先將自簽名 CA 證書匯入到客戶端的作業系統可信任區,這樣在 TLS 握手過程中也能自動獲取到這個 CA 證書。

另外:驗證證書在 SSL/TLS 協議中不一定是必須的,比如 mysql 客戶端只有指定 --ssl-mode=VERIFY_CA 或者 --ssl-mode=VERIFY_IDENTITY 時才驗證 CA 證書。如果 --ssl-mode=REQUIRED,則不驗證 CA 證書,只要求 MySQL Server 端傳送公鑰給客戶端,這就無法保證服務端公鑰是真實屬於 MySQL server 的。詳細見下文。

2. MySQL SSL 加密連線

2.1 MySQL服務端的配置

啟動引數:

  • --ssl:表示 MySQL 服務端允許加密連線,這個啟動引數MySQL8.0預設啟用

系統變數:

  • require_secure_transport:指定是否要求客戶端使用加密連線。預設值為 OFF,如果 ON,則表示客戶端必須使用加密連線,如果客戶端關閉 ssl ,則連線會報錯。

以下引數指定加密連線時使用的證書和金鑰檔案:

ssl_ca=ca.pem
ssl_cert=server-cert.pem
ssl_key=server-key.pem

MySQL8.0 在啟動時會自動生成 SSL 證書、金鑰檔案,以及 RSA 金鑰對檔案;或者使用 mysql_ssl_rsa_setup 程式生成上述檔案。也可以手工生成:

## SSL證書和金鑰檔案
certs
├── ca-key.pem
├── ca.pem
├── client-cert.pem
├── client-key.pem
├── server-cert.pem
└── server-key.pem

# Create CA certificate
# 建立CA證書(包含CA公鑰)和CA私鑰
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 3600 \
        -key ca-key.pem -out ca.pem

# Create server certificate, remove passphrase, and sign it
# server-cert.pem = public key, server-key.pem = private key
# 先生成伺服器公鑰、私鑰
# 使用CA私鑰對伺服器公鑰簽名,得到伺服器證書 server-cert.pem,證書中包含公鑰、所有者、有效期等明文資訊,也有經過 CA 私鑰加密對公鑰、所有者、有效期...加密後的簽名
openssl req -newkey rsa:2048 -days 3600 \
        -nodes -keyout server-key.pem -out server-req.pem
openssl rsa -in server-key.pem -out server-key.pem
openssl x509 -req -in server-req.pem -days 3600 \
        -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem

# Create client certificate, remove passphrase, and sign it
# client-cert.pem = public key, client-key.pem = private key
# 先生成客戶端公鑰、私鑰
# 使用CA私鑰對客戶端公鑰簽名,得到客戶端證書 client-cert.pem,一般不需要驗證客戶端身份,這些檔案就不需要用到。當然如果要同時驗證 MySQL Server 身份和客戶端身份,就需要用到這些檔案了。
openssl req -newkey rsa:2048 -days 3600 \
        -nodes -keyout client-key.pem -out client-req.pem
openssl rsa -in client-key.pem -out client-key.pem
openssl x509 -req -in client-req.pem -days 3600 \
        -CA ca.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem

# 檢視證書內容
openssl x509 -text -in ca.pem

# 驗證CA證書(“使用CA證書驗證數字證書”更恰當?)
openssl verify -CAfile ca.pem server-cert.pem
# 輸出結果:server-cert.pem: OK

2.2 MySQL客戶端配置

MySQL 客戶端連線 Server 時,通過 --ssl-mode 引數指定:

  • --ssl-mode=PREFFERED,預設行為,client 端嘗試使用加密進行連線,如果無法構建加密連線,則會退回到未加密的連線
  • --ssl-mode=REQUIRED時,Client 端需要加密連線,如果無法構建連線,則 Client 端將失敗
  • --ssl-mode=DISABLED,Client 端使用未加密的連線
  • --ssl-mode=VERIFY_CA,Client 端需要加密連線,並且還對 CA 證書進行驗證
  • --ssl-mode=VERIFY_IDENTITY,Client 端需要加密的連線,並且還針對 CA 證書和其證書中的伺服器主機名執行驗證

注意:主機名身份驗證 VERIFY_IDENTITY 不適用於由伺服器自動建立或使用 mysql_ssl_rsa_setup 手動建立的自簽名CA證書。

測試如下:

##當指定 --ssl-mode=REQUIRED,僅要求加密連線,不需要驗證 MySQL Server 身份,所以會直接信任 MySQL Server 傳送給客戶端的公鑰(即 server-cert.pem 數字證書中的明文公鑰,忽略其中的數字簽名資訊)
[root@172-16-21-5 /]# /opt/mysql/base/8.0.21/bin/mysql -h172.16.21.4 -P3306 -utest -ptestpass --ssl-mode=REQUIRED -e "select 1"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---+
| 1 |
+---+
| 1 |
+---+

##當指定 --ssl-mode=VERIFY_CA,需要驗證 CA 證書,因為這個 CA 證書是自簽發的,所以不在瀏覽器和作業系統的可信任區,無法從瀏覽器和作業系統的可信任區這個公共渠道獲取 CA 證書,所以報錯:
[root@172-16-21-5 /]# /opt/mysql/base/8.0.21/bin/mysql -h172.16.21.4 -P3306 -utest -ptestpass --ssl-mode=VERIFY_CA
ERROR 2026 (HY000): SSL connection error: CA certificate is required if ssl-mode is VERIFY_CA or VERIFY_IDENTITY

##將MySQL服務端的 ca.pem 拷貝到客戶端
scp ca.pem 172.16.21.5:/tmp/

##--ssl-mode=VERIFY_CA,指定需要驗證 CA 證書,因為這個 CA 證書是自簽發的,所以不在瀏覽器和作業系統的可信任區,則必須要將 CA 證書拷貝到客戶端,並指定 CA 證書檔案
##TLS 握手過程中,MySQL Server 傳送伺服器數字證書 server-cert.pem 給客戶端,客戶端使用 CA 證書中的 CA 公鑰解密 server-cert.pem 中的簽名,驗證通過,才可以正常登陸:
[root@localhost ~]# mysql -h10.186.61.173 -P3308 -uhucq -p'1qaz@WSX' \
--ssl-ca="/tmp/ca.pem" \
--ssl-mode=VERIFY_CA \
-e "select 1"
+---+
| 1 |
+---+
| 1 |
+---+

##由於MySQL自動生成的CA證書是自簽名證書,而 --ssl-mode=VERIFY_IDENTITY 不適用於由伺服器自動建立或使用 mysql_ssl_rsa_setup手動建立的自簽名CA證書,即使指定本地的CA證書檔案,連線也會失敗
[root@localhost ~]# mysql -h10.186.61.173 -P3308 -uhucq -p'1qaz@WSX' \
--ssl-ca="/tmp/ca.pem" \
--ssl-mode=VERIFY_IDENTITY \
-e "select 1"
ERROR 2026 (HY000): SSL connection error: Failed to verify the server certificate via X509 certificate matching functions

2.3 MySQL SSL 連線中的 TLS 握手過程

上述示例已有詳細說明,這裡再簡要總結一下:

  1. 客戶端發起 ssl 連線請求;
  2. MySQL Server 傳送數字證書 server-cert.pem 給客戶端(server-cert.pem包含:伺服器公鑰、CA簽名資訊);
  3. 客戶端使用CA 證書 ca.pem(由於這是 MySQL 自簽名的CA證書,無法從作業系統的可信任區獲取(壓根不在這裡邊),所以事先必須在客戶端本地儲存 CA 證書檔案)中的 CA 公鑰解密 server-cert.pem 中的簽名,進行驗證;
  4. 驗證通過後,生成對稱金鑰,使用 server-cert.pem 中的公鑰加密“對稱金鑰”,傳送給 MySQL Server;
  5. MySQL Server 使用自己保留的私鑰 server-key.pem 解密,得到“對稱金鑰”;
  6. 接下來傳輸資料則使用“對稱金鑰”進加密和解密。

如果僅指定 --ssl-mode=REQUIRED,不指定 --ssl-mode=VERIFY_CA 或者 --ssl-mode=VERIFY_IDENTITY,則不需要步驟3。

3. JDBC 如何設定SSL連線

首先 MySQL Server 端必須生成 SSL 證書和金鑰檔案,並且在啟動時指定啟動引數:--ssl(一般將其寫到 my.cnf 中)。MySQL8.0 啟動時會自動生成SSL 證書和金鑰檔案,並預設使用 --ssl 引數。

JDBC 關閉 ssl 連線示例:jdbc:mysql://localhost:3306/hucq?useSSL=false

如果 MySQL Server 使用 caching_sha2_password(MySQL8.0預設的認證外掛)、sha256_password 認證外掛,則還必須指定 AllowPublicKeyRetrieval=True,因為 caching_sha2_password 外掛要求交換密碼時必須使用 RSA 公鑰加密(在沒有使用SSL加密連線的情況下),AllowPublicKeyRetrieval=True 引數作用是請求 MySQL Server 端傳送 RSA 公鑰給客戶端,如果不請求 RSA 公鑰並且又沒有指定客戶端本地RSA公鑰檔案(先從 MySQL 伺服器上拷貝 RSA 公鑰到本地),則連線會報錯。應該配置:jdbc:mysql://localhost:3306/hucq?useSSL=false&AllowPublicKeyRetrieval=True

JDBC 開啟 SSL 連線,意味著要求安全連線,則應該開啟 CA 證書認證,跟 mysql 客戶端一樣,需要將 MySQL 的自簽名 CA 證書匯入到客戶端,或者放到 ftp 上,再通過 JDBC 引數指定 CA 證書路徑,比較複雜,請參考官方文件:https://dev.mysql.com/doc/con...

參考資料

  1. https://tangyuxian.com/2021/0...
  2. https://dev.mysql.com/doc/ref...

相關文章