1. 概述
未加密的 MySQL 服務器與客户端之間的連接可能暴露網絡傳輸中的數據。對於生產環境的應用,我們應該將所有通信轉移到通過 TLS(傳輸層安全)協議的加密連接中。
在本教程中,我們將學習如何在一個 MySQL 服務器上啓用安全連接。此外,我們還將配置 Spring Boot 應用程序以使用此安全連接。
2. 使用 TLS 的原因
首先,讓我們瞭解一些 TLS 的基本概念。
TLS 協議使用加密算法,以確保通過網絡傳輸的數據可以得到信任,並且不會被篡改或監視。它具有檢測數據更改、丟失或重放攻擊的機制。 TLS 還包含使用 X.509 標準提供的身份驗證算法。
加密連接會增加一層安全保障,使網絡流量中的數據無法被讀取。
配置 MySQL 服務器和客户端之間的安全連接,可以實現更好的身份驗證、數據完整性和可信度。 此外,MySQL 服務器還可以對客户端的身份進行額外的檢查。
然而,這種安全連接會帶來性能懲罰,因為加密會消耗計算資源。 性能成本的嚴重程度取決於多種因素,例如查詢大小、數據負載、服務器硬件、網絡帶寬和其他因素。
3. 配置 MySQL 服務器上的 TLS 連接
MySQL 服務器以連接為基礎進行加密,並且可以針對特定用户強制或可選地啓用此功能。MySQL 支持通過安裝的 OpenSSL 庫在運行時執行與 SSL 加密相關的操作。
我們可以使用 JDBC 驅動程序 Connector/J 在初始握手之後,對客户端和服務器之間的數據進行加密。
MySQL 8.0.28 或更高版本僅支持 TLS v1.2 和 TLS v1.3。它不再支持較早版本的 TLS (v1 和 v1.1)。
服務器身份驗證可以啓用,使用通過受信任的根證書頒發機構簽發的證書或自簽名證書。
此外,通常的做法是在生產環境中構建自己的根 CA 文件。
服務器還可以驗證客户端的 SSL 證書並對客户端身份執行額外的檢查。
3.1. 使用 TLS 證書配置 MySQL 服務器
我們將使用 <em require_secure_transport</em> 屬性和默認生成的證書,在 MySQL 服務器上啓用安全傳輸。
以下是如何通過 <em docker-compose.yml</em> 文件快速啓動 MySQL 服務器的示例:
version: '3.8'
services:
mysql-service:
image: "mysql/mysql-server:8.0.30"
container_name: mysql-db
command: [ "mysqld",
"--require_secure_transport=ON",
"--default_authentication_plugin=mysql_native_password",
"--general_log=ON" ]
ports:
- "3306:3306"
volumes:
- type: bind
source: ./data
target: /var/lib/mysql
restart: always
environment:
MYSQL_ROOT_HOST: "%"
MYSQL_ROOT_PASSWORD: "Password2022"
MYSQL_DATABASE: test_db我們應注意,上述MySQL Server 使用默認證書,位於路徑 /var/lib/mysql 。
或者,我們可以通過在 docker-compose.yml 中包含一些 mysqld 配置來覆蓋默認證書。
command: [ "mysqld",
"--require_secure_transport=ON",
"--ssl-ca=/etc/certs/ca.pem",
"--ssl-cert=/etc/certs/server-cert.pem",
"--ssl-key=/etc/certs/server-key.pem",
....]現在,讓我們使用 mysql-service 命令,並使用 docker-compose 命令:
$ docker-compose -p mysql-server up3.2. 創建用户,使用 X509
可選地,我們可以使用 X509 標準配置 MySQL 服務器,並啓用客户端身份識別。 使用 X509,需要提供有效的客户端證書。 這允許啓用雙向互信 TLS(mTLS)。
下面創建一個使用 X509 並授予對 test_db 數據庫的權限的用户:
mysql> CREATE USER 'test_user'@'%' IDENTIFIED BY 'Password2022' require X509;
mysql> GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'%';
我們可以在不進行任何用户證書識別的情況下建立 TLS 連接:
mysql> CREATE USER 'test_user'@'%' IDENTIFIED BY 'Password2022' require SSL;需要注意的是,如果使用SSL,客户端必須提供信任庫。
4. 配置 Spring Boot 應用程序的 TLS
Spring Boot 應用程序可以通過設置 JDBC URL 並指定一些屬性來配置 JDBC 連接上的 TLS。
有多種方法可以配置 Spring Boot 應用程序使用 TLS 與 MySQL 連接。
在此之前,我們需要將 truststore 和客户端證書轉換為 JKS 格式。
4.1. 將 PEM 文件轉換為 JKS 格式
讓我們將 MySQL 服務器生成的 ca.pem 和 client-cert.pem 文件轉換為 JKS 格式:
keytool -importcert -alias MySQLCACert.jks -file ./data/ca.pem \
-keystore ./certs/truststore.jks -storepass mypassword
openssl pkcs12 -export -in ./data/client-cert.pem -inkey ./data/client-key.pem \
-out ./certs/certificate.p12 -name "certificate"
keytool -importkeystore -srckeystore ./certs/certificate.p12 -srcstoretype pkcs12 -destkeystore ./certs/client-cert.jks需要注意的是,自Java 9版本起,默認密鑰庫格式為PKCS12
4.2. 使用 application.yml進行配置
TLS 可以通過將 sslMode 設置為 PREFERRED, REQUIRED, VERIFY_CA 或 VERIFY_IDENTITY 來啓用。
PREFERRED 模式會在服務器支持的情況下使用安全連接,否則會回退到未加密連接。
使用 REQUIRED 模式 時,客户端只能使用加密連接。 類似於 REQUIRED 和 VERIFY_CA 模式,VERIFY_CA 模式使用安全連接,並且還通過驗證服務器證書與配置的證書頒發機構 (CA) 證書來驗證。
VERIFY_IDENTITY 模式還會在證書驗證之外,對主機名進行額外的檢查。
還需要向 JDBC URL 添加一些 Connector/J 屬性,例如 trustCertufucateKeyStoreUrl, trustCertificateKeyStorePassword, clientCertificateKeyStoreUrl 和 clientCertificateKeyStorePassword。
讓我們在 application.yml 中配置 JDBC URL,並將 sslMode 設置為 VERIFY_CA:
spring:
profiles: "dev2"
datasource:
url: >-
jdbc:mysql://localhost:3306/test_db?
sslMode=VERIFY_CA&
trustCertificateKeyStoreUrl=file:/<project-path>/mysql-server/certs/truststore.jks&
trustCertificateKeyStorePassword=mypassword&
clientCertificateKeyStoreUrl=file:/<project-path>/mysql-server/certs/client-cert.jks&
clientCertificateKeyStorePassword=mypassword
username: test_user
password: Password2022請注意,已棄用的屬性 實際上是 和 的組合。
如果未提供信任證書文件,將會出現相應的錯誤。
Caused by: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
at java.base/sun.security.provider.certpath.PKIXCertPathValidator.validate(PKIXCertPathValidator.java:157) ~[na:na]
at java.base/sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:83) ~[na:na]
at java.base/java.security.cert.CertPathValidator.validate(CertPathValidator.java:309) ~[na:na]
at com.mysql.cj.protocol.ExportControlled$X509TrustManagerWrapper.checkServerTrusted(ExportControlled.java:402) ~[mysql-connector-java-8.0.29.jar:8.0.29]
如果缺少客户端證書,將會收到不同的錯誤:
Caused by: java.sql.SQLException: Access denied for user 'test_user'@'172.20.0.1'4.3. 使用環境變量配置 TLS
或者,我們可以將上述配置設置為環境變量,並將與 SSL 相關的配置作為 JVM 參數包含進來。
讓我們將 TLS 和 Spring 相關的配置添加到環境變量中:
export TRUSTSTORE=./mysql-server/certs/truststore.jks
export TRUSTSTORE_PASSWORD=mypassword
export KEYSTORE=./mysql-server/certs/client-cert.jks
export KEYSTORE_PASSWORD=mypassword
export SPRING_DATASOURCE_URL=jdbc:mysql://localhost:3306/test_db?sslMode=VERIFY_CA
export SPRING_DATASOURCE_USERNAME=test_user
export SPRING_DATASOURCE_PASSWORD=Password2022然後,讓我們使用上述 SSL 配置運行該應用程序:
$java -Djavax.net.ssl.keyStore=$KEYSTORE \
-Djavax.net.ssl.keyStorePassword=$KEYSTORE_PASSWORD \
-Djavax.net.ssl.trustStore=$TRUSTSTORE \
-Djavax.net.ssl.trustStorePassword=$TRUSTSTORE_PASSWORD \
-jar ./target/spring-boot-mysql-0.1.0.jar5. 驗證 TLS 連接
現在,讓我們使用上述任何一種方法運行應用程序,並驗證 TLS 連接。
可以使用 MySQL 服務器的通用日誌或查詢 <span lang="en">process</span> 和 <span lang="en">sys</span> admin 表來驗證 TLS 連接。
讓我們使用日誌文件在默認路徑下進行驗證:/var/lib/mysql/
$ cat /var/lib/mysql/7f44397082d7.log
2022-09-17T13:58:25.887830Z 19 Connect [email protected] on test_db using SSL/TLS當然,以下是翻譯後的內容:
或者,我們來驗證 test_user 使用的連接:
mysql> SELECT process.thd_id,user,db,ssl_version,ssl_cipher FROM sys.processlist process, sys.session_ssl_status session where process.user='[email protected]'and process.thd_id=session.thread_id;+--------+----------------------+---------+-------------+------------------------+ | thd_id | user | db | ssl_version | ssl_cipher | +--------+----------------------+---------+-------------+------------------------+ | 167 | [email protected] | test_db | TLSv1.3 | TLS_AES_256_GCM_SHA384 | | 168 | [email protected] | test_db | TLSv1.3 | TLS_AES_256_GCM_SHA384 | | 169 | [email protected] | test_db | TLSv1.3 | TLS_AES_256_GCM_SHA384 |
6. 結論
在本文中,我們學習了 TLS 連接到 MySQL 如何確保網絡上傳輸的數據安全。 此外,我們還了解了如何在 Spring Boot 應用程序中配置 TLS 連接到 MySQL 服務器。