MySQL如何配置SSL连接

wen 网络安全 1

MySQL SSL连接配置完全指南:从入门到生产级部署

目录导读

  1. SSL连接的核心价值与场景
  2. 前置环境准备与证书生成
  3. MySQL服务端SSL配置详解
  4. 客户端SSL连接配置方法
  5. 强制SSL连接策略与验证
  6. 常见问题与性能调优
  7. 问答环节

MySQL如何配置SSL连接

SSL连接的核心价值与场景

MySQL默认传输数据是明文的,这意味着任何能窃听网络流量的人都能看到你的SQL语句和返回结果,SSL(Secure Sockets Layer)加密连接能解决三个核心问题:

  • 数据机密性:防止中间人攻击,确保传输内容不被窃听
  • 数据完整性:防止数据在传输过程中被篡改
  • 身份验证:通过数字证书验证服务器身份,防止DNS欺骗或ARP攻击

适用场景包括:跨公网数据库访问、金融/医疗等合规要求严格的环境、多数据中心同步、混合云架构等。

问答1:为什么生产环境必须开启SSL?
:未加密的MySQL连接会暴露所有SQL语句(包含密码、用户数据)、查询结果集,一旦网络被监听(例如公共WiFi、ISP监控),攻击者可直接窃取数据,SSL是满足GDPR、PCI DSS等合规要求的必要措施。


前置环境准备与证书生成

1 检查MySQL SSL支持

# 查看是否已编译SSL模块
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"

若显示have_ssl=YES则直接可用;若为DISABLED,需检查MySQL编译参数或升级版本。

2 使用OpenSSL生成自签名证书(离线环境推荐)

# 创建证书目录
mkdir -p /etc/mysql/ssl && cd /etc/mysql/ssl
# 1. 生成CA私钥(有效期5年)
openssl genrsa 2048 > ca-key.pem
# 2. 生成CA证书
openssl req -new -x509 -nodes -days 1825 -key ca-key.pem -out ca-cert.pem \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/CN=MySQL-CA"
# 3. 生成服务器私钥和证书请求
openssl req -newkey rsa:2048 -nodes -days 365 -keyout server-key.pem -out server-req.pem \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/CN=mysql-server"
# 4. 用CA签名服务器证书
openssl x509 -req -in server-req.pem -days 365 -CA ca-cert.pem -CAkey ca-key.pem \
  -set_serial 01 -out server-cert.pem
# 5. 生成客户端证书(用于双向认证,可选)
openssl req -newkey rsa:2048 -nodes -days 365 -keyout client-key.pem -out client-req.pem \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/CN=mysql-client"
openssl x509 -req -in client-req.pem -days 365 -CA ca-cert.pem -CAkey ca-key.pem \
  -set_serial 02 -out client-cert.pem
# 6. 设置权限
chmod 600 server-key.pem client-key.pem ca-key.pem

若使用云服务商(如AWS RDS、阿里云RDS),通常支持一键启用SSL并自动提供证书下载,无需手动生成。

3 检查证书有效性

openssl verify -CAfile ca-cert.pem server-cert.pem
# 输出应为: server-cert.pem: OK

问答2:自签名证书和商业CA证书的区别?
:自签名证书免费,但需客户端配置--ssl-mode=REQUIRED并信任CA,商业CA证书(如DigiCert、Let's Encrypt)由浏览器/系统预置信任列表,适合面向公网的服务,内网环境推荐自签名证书,并用自动化工具(如certbot)定期轮换。


MySQL服务端SSL配置详解

1 配置my.cnf

编辑MySQL配置文件(通常位于/etc/my.cnf/etc/mysql/mysql.conf.d/mysqld.cnf):

[mysqld]
# SSL路径
ssl-ca=/etc/mysql/ssl/ca-cert.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem
# 可选:指定加密协议及密码套件(提高安全性)
ssl-cipher=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
tls-version=TLSv1.2,TLSv1.3
# 强制SSL连接(见后续章节)
# require_secure_transport=ON

2 重启MySQL并验证

systemctl restart mysql
# 或 service mysqld restart
# 确认SSL状态
mysql -u root -p -e "SHOW VARIABLES WHERE Variable_name IN ('have_ssl','ssl_ca','ssl_cert','ssl_key');"

期望输出:

+----------------+-----------------------------------+
| Variable_name  | Value                             |
+----------------+-----------------------------------+
| have_ssl       | YES                               |
| ssl_ca         | /etc/mysql/ssl/ca-cert.pem        |
| ssl_cert       | /etc/mysql/ssl/server-cert.pem    |
| ssl_key        | /etc/mysql/ssl/server-key.pem     |
+----------------+-----------------------------------+

3 检查当前所有连接的加密状态

SHOW STATUS LIKE 'Ssl_cipher';
-- 如果显示非空(如TLS_AES_256_GCM_SHA384),说明至少有一个连接使用了加密

客户端SSL连接配置方法

1 MySQL命令行客户端

# 强制使用SSL,并验证服务器证书
mysql -h db.example.com -u app_user -p \
  --ssl-ca=/path/to/ca-cert.pem \
  --ssl-mode=VERIFY_CA
# 简化模式(仅加密,不验证证书)
mysql -h db.example.com -u app_user -p --ssl-mode=REQUIRED

2 应用程序连接配置

Java (JDBC)

String url = "jdbc:mysql://db.example.com:3306/mydb?useSSL=true&serverSslCert=/path/to/ca-cert.pem&requireSSL=true";

Python (mysql-connector-python)

import mysql.connector
conn = mysql.connector.connect(
    host="db.example.com",
    user="app_user",
    password="xxx",
    ssl_ca="/path/to/ca-cert.pem",
    ssl_verify_cert=True
)

PHP (PDO)

$pdo = new PDO(
    'mysql:host=db.example.com;dbname=mydb',
    'app_user',
    'xxx',
    array(
        PDO::MYSQL_ATTR_SSL_CA => '/path/to/ca-cert.pem',
        PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
    )
);

3 使用SSL连接后验证

-- 查看当前会话加密算法
SHOW STATUS LIKE 'Ssl_cipher';
-- 示例输出: Ssl_cipher | TLS_AES_256_GCM_SHA384

问答3:客户端--ssl-mode参数不同等级的区别?
:MySQL 8.0支持6种SSL模式:

  • DISABLED:不加密
  • PREFERRED:优先加密,若不可用则回退明文
  • REQUIRED:强制加密,但不验证服务器证书
  • VERIFY_CA:加密并验证服务器证书是否由受信任CA签发
  • VERIFY_IDENTITY:在VERIFY_CA基础上,额外验证主机名是否匹配证书CN/SAN
    生产环境至少应使用VERIFY_CA,强烈建议VERIFY_IDENTITY防止中间人攻击。

强制SSL连接策略与验证

1 全局强制SSL

在my.cnf中添加:

[mysqld]
require_secure_transport=ON

重启后,所有不使用SSL的连接将被拒绝。

2 针对特定用户强制SSL

-- 创建用户时强制SSL(推荐)
CREATE USER 'secure_user'@'%' IDENTIFIED BY 'password' REQUIRE SSL;
-- 或修改现有用户
ALTER USER 'existing_user'@'%' REQUIRE SSL;
-- 强制使用特定密码套件(进阶)
ALTER USER 'user'@'%' REQUIRE SUBJECT '/C=CN/ST=Beijing/O=MyCompany/CN=mysql-client';
-- 检查用户SSL要求
SELECT user, host, ssl_type FROM mysql.user WHERE user='secure_user';

输出ssl_typeANY表示强制SSL。

3 验证强制策略是否生效

# 尝试无SSL连接,应被拒绝
mysql -u secure_user -p -h db.example.com --ssl-mode=DISABLED
# 错误: ERROR 1045 (28000): Access denied for user 'secure_user'@'...' (using password: YES)

常见问题与性能调优

1 常见错误排查

错误信息 原因 解决方案
SSL connection error: SSL_CTX_set_default_verify_paths failed 系统缺少CA证书路径 设置--ssl-capath或检查ca-cert.pem不可读
ERROR 2026 (HY000): SSL connection error: unknown error number 证书格式错误或权限不足 检查文件权限为600,并确保MySQL用户可读取
The server is not configured for SSL MySQL未启用SSL 确认have_ssl=YES,重启服务

2 性能影响与优化

SSL加密会引入CPU开销,实测约5%-15%的性能下降(取决于CPU性能和查询复杂度),优化建议:

  • 使用TLS 1.3(比1.2减少1次握手,同时降低延迟30%)
  • 优先选择AES-GCM或ChaCha20-Poly1305等硬件加速套件
  • 在连接池层面启用SSL,避免频繁握手
  • 对于内网高并发场景,可考虑仅对敏感操作加密(但会增加管理复杂度)

3 证书定期轮换脚本框架

#!/bin/bash
# 每周自动轮换自签名证书(需配合cron)
cd /etc/mysql/ssl
openssl genrsa 2048 > server-key-new.pem
openssl req -new -key server-key-new.pem -out server-req-new.pem -subj "..."
openssl x509 -req -in server-req-new.pem -CA ca-cert.pem -CAkey ca-key.pem -set_serial $RANDOM -out server-cert-new.pem
# 替换文件并重启服务
cp server-key-new.pem server-key.pem
cp server-cert-new.pem server-cert.pem
systemctl restart mysql

问答环节

Q1:为什么我的SSL配置正确,但客户端显示Ssl_cipher为空?
:检查MySQL的require_secure_transport是否开启,如果你的配置是PREFERRED,客户端可能回退到明文连接,建议在客户端连接时显式指定--ssl-mode=REQUIRED

Q2:使用VERIFY_CA模式时,如何避免DNS劫持攻击?
:使用VERIFY_IDENTITY(MySQL 8.0.13+),它会额外校验客户端请求的主机名与证书的CN(Common Name)或SAN(Subject Alternative Name)是否匹配,建议在证书生成时使用-ext "subjectAltName=DNS:db.example.com"

Q3:高并发场景下SSL握手速度慢怎么办?
:第一个关键点是启用TLS 1.3(握手只需1-RTT);第二个是配置连接池(如HikariCP、Druid)保持长连接,避免频繁握手;第三个是使用硬件SSL加速卡或QAT(QuickAssist Technology)设备。

Q4:免费证书Let's Encrypt可以用于MySQL SSL吗?
:可以,但需注意Let's Encrypt证书有效期仅90天,建议使用自动化脚本(如certbot的--post-hook)自动更新证书并重启MySQL,对于公网访问的数据库,推荐使用云服务商提供的托管SSL。

Q5:如何验证数据库是否真的使用了加密传输(而非客户端缓存)?
:在MySQL服务器上执行SHOW PROCESSLIST;,观察Host列旁边的加密状态指示符,或在客户端抓包(需root权限):tcpdump -i eth0 port 3306 -A | head -100,加密后的数据应为不可读的乱码。


延伸阅读

  • MySQL官方文档:SSL/TLS配置
  • OpenSSL Cookbook:自签名证书最佳实践
  • 云服务商文档(AWS RDS SSL / 阿里云SSL加密)

抱歉,评论功能暂时关闭!