- 背景
因其他变更需要重启数据库,随后业务启动发现异常。
- 环境版本
MySQL8.0.15
- 现象
业务报障:Public Key Retrieval is not allowed
报错截图:
- 原因分析
此时在数据库查看业务账号连接状态皆为"Receiving from client" 状态
排查方向一、ssl检查
网上的答案很多都是修改代码配置项:
解决此异常问题需要将allowPublicKeyRetrieval=true和useSSL=false。
但是我这边只是重启了数据库,业务侧并没有任何的改动。因此不应该从修改代码配置这里解决问题。
查看ssl参数是启用的
排查方向二、检查身份认证插件
- 问题根因
- 这套数据库不是标准化安装的,所以在8.0环境中默认使用了caching_sha2_password插件(标准化场景中默认插件沿用5.7的mysql_native_password)
- 数据库重启后,业务账号没有登录缓存。在禁用SSL/TLS协议传输且当前用户在服务器端没有登录缓存的情况下,客户端没有办法拿到服务器的公钥,出现上面的报错。
出现该问题的时候,MySQL配置的密码认证插件为如下两种:
- sha256_password
- caching_sha2_password
值得注意的是,如果使用“mysql_native_password”密码认证插件,不会出现“Public Key Retrieval is not allowed”错误。
首先MySQL 8.0默认推荐使用“sha256_password”和“caching_sha2_password”这两种认证插件。只有较老的MySQL版本仍然会使用“mysql_native_password”。
“Public Key Retrieval is not allowed”错误产生的原因
首先MySQL 8.0默认推荐使用“sha256_password”和“caching_sha2_password”这两种认证插件。只有较老的MySQL版本仍然会使用“mysql_native_password”。
根据MySQL提供的官方文档(https://dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html),这两种插件都是使用SHA256算法来对密码进行保护。这些插件的具体执行流程如下:
- 检查客户端是否禁用SSL/TLS加密传输;
- 如果客户端未禁用SSL/TLS加密传输,则客户端在进行认证时的认证报文(传输用户名和密码的报文)是使用TLS进行传输的,两种插件认为认证报文传输安全,不进行任何其他操作;
- 如果客户端禁用SSL/TLS加密传输,则客户端在进行认证时的认证报文(传输用户名和密码的报文)是使用明文进行传输的,两种插件认为认证报文传输不安全,会单独对明文报文中的密码使用RSA加密方式进行加密。
SSL/TLS认证流程
- 启用SSL/TLS加密传输的客户端认证流程
如上述流程所述,当客户端未禁用SSL/TLS加密传输时,通过Wireshark等抓包工具可以观察到整个客户端与MySQL服务端交互的过程都被TLS协议加密保护了。
虽然会产生登录的明文报文,但是该明文报文中的用户信息为空,真正的用户信息在TLS握手阶段后的密文中。如下图所示:
- 禁用SSL/TLS加密传输的客户端认证流程
如上述流程所述,当客户端禁用SSL/TLS加密传输时(比如JDBC连接串设置useSSL=false参数),用户的登录信息会在明文中进行传输
明文传输密码存在安全问题,此时,两种插件会尝试使用RSA加密(RSA encryption)方法对明文报文中的密码部分进行加密,加密后的密码部分如下图所示:
根据前面的分析,导致“Public Key Retrieval is not allowed”主要是由于当禁用SSL/TLS协议传输后,客户端会使用服务器的公钥进行传输,默认情况下客户端不会主动去找服务器拿公钥,此时就会出现上述错误。
经过查阅官方文档,出现Public Key Retrieval的场景可以概括为在禁用SSL/TLS协议传输且当前用户在服务器端没有登录缓存的情况下,客户端没有办法拿到服务器的公钥。具体的场景如下:
- 新建数据库用户,首次登录;
- 数据库的用户名、密码发生改变后登录;
- 服务器端调用FLUSH PRIVELEGES指令刷新服务器缓存。
针对上述错误,有如下的解决方案:
一、使用“sha256_password”和“caching_sha2_password”这两种认证插件时,代码侧需要改动:
- 在条件允许的情况下,不要禁用SSL/TLS协议,即不要在CLI客户端使用--ssl-mode=disabled,或在JDBC连接串中加入useSSL=false;
- 如果必须禁用SSL/TLS协议,则可以尝试使用CLI客户端登录一次MySQL数据库制造登录缓存;
- 如果必须禁用SSL/TLS协议,则可以通过增加如下参数允许客户端获得服务器的公钥:
- 在JDBC连接串中加入allowPublicKeyRetrieval=true参数;
- 在CLI客户端连接时加入--get-server-public-key参数;
- 在CLI客户端连接时加入--server-public-key-path=file_name参数,指定存放在本地的公钥文件。
二、使用mysql_native_password插件,数据库侧需要改动:
- 修改有问题的用户密码,使用旧版的mysql_native_password插件。
- 设置默认认证插件为旧版的mysql_native_password插件。
本次问题的解决办法
- 卸载了密码插件validate_password。
- 创建了新的业务账号做替换,使用了旧版的mysql_native_password插件。




