Python pymysql库ssl_cert方法连接数据库时证书验证失败怎么办?

问题现象描述

当开发者使用pymysql库的ssl_cert参数建立SSL加密数据库连接时,常见会遇到以下报错:

pymysql.err.OperationalError: (2026, 'SSL connection error: certificate verify failed')

该错误表明客户端无法验证服务器提供的SSL证书有效性,通常发生在以下几种场景:

  • 自签名证书未添加到信任存储
  • 证书链不完整(缺少中间CA证书)
  • 证书已过期或尚未生效
  • 主机名与证书CN/SAN不匹配

根本原因分析

SSL/TLS握手过程中,客户端会进行严格的证书验证:

  1. 信任锚验证:检查证书是否由受信任的CA签发(默认使用系统CA存储)
  2. 有效期验证:证书必须在有效时间范围内
  3. 主机名验证:连接的主机名必须匹配证书中的CN或SAN字段

通过Wireshark抓包分析可见,当证书验证失败时,TLS握手会在Server Certificate阶段中断。

解决方案

方案1:禁用证书验证(仅测试环境)

conn = pymysql.connect(
    ssl={'cert': '/path/client-cert.pem'},
    ssl_disabled=True  # 禁用验证
)

⚠️ 生产环境严禁使用此方案,会丧失SSL加密的保护作用

方案2:正确配置证书路径

需确保以下文件路径正确:

ssl={
    'cert': 'client-cert.pem',
    'key': 'client-key.pem',
    'ca': 'server-ca.pem'  # 必须包含完整的证书链
}

方案3:调试证书问题

使用openssl验证证书链:

openssl verify -CAfile server-ca.pem server-cert.pem

检查证书详细信息:

openssl x509 -in server-cert.pem -text -noout

高级排查技巧

问题类型 诊断命令 解决方案
证书过期 openssl x509 -dates 重新签发证书
主机名不匹配 比对证书SAN与连接URL 使用正确主机名或配置skip_hostname_verify

最佳实践建议

  • 使用Let's Encrypt等权威CA签发证书
  • 定期轮换证书(建议不超过90天)
  • 在连接字符串中显式指定ssl_mode参数:
ssl_mode="VERIFY_IDENTITY"  # 最严格验证级别