MySQL身份验证插件(mysql_old_password、mysql_native_password、sha256_password、caching_sha2_password)

配置文件加密选项:

1
2
3
4
[mysqld]
default-authentication-plugin=mysql_native_password
default-authentication-plugin=mysql_old_password
default-authentication-plugin=sha256_password

命令行配置:

1
2
3
set old_passwords=0 /* mysql_native_password*/
set old_passwords=1 /* mysql_old_password */
set old_passwords=2 /* sha256_password */

创建用户并指定插件sha256_password给该用户

1
2
3
CREATE USER 'sha256user'@'localhost' IDENTIFIED BY 'password';
#set old_passwords=2 /* sha256_password */
SET PASSWORD FOR 'sha256user'@'localhost' = PASSWORD('password');

如果要设置为非默认插件mysql_native_password,必须显示指定插件名称, 且在设置密码前要设置old_passwords为相应的值。

1
2
3
CREATE USER 'nativeuser'@'localhost' IDENTIFIED WITH mysql_native_password;
SET old_passwords=0 /* mysql_native_password*/
SET PASSWORD FOR 'nativeuser'@'localhost' = PASSWORD('N@tivePa33');

如果old_passwords设置和插件不一致,修改密码时就会报错

1
2
3
4
5
mysql> SET old_passwords = 0;
mysql> SET PASSWORD FOR 'sha256user'@'localhost' = PASSWORD('password');
ERROR 1827 (HY000): The password hash doesn't have the expected format.
Check if the correct password algorithm is being used with the
PASSWORD() function.

客户端通过--default-auth选项指定插件,--secure-auth选项开启可禁用旧格式的密码

1
mysql --default-auth=mysql_native_password --secure-auth=ON

PASSWORD在MySQL 4.1以前的哈希方法生成一个16字节的字符串,4.1之后生成是41字节的哈希值 4.1格式的密码散列始终以*字符开头,而4.1之前的格式的密码则永远不会以字符开头。 4.1之前的密码散列和mysql_old_password插件在MySQL 5.6中已弃用,而对它们的支持在MySQL 5.7中已删除。

mysql_old_password

4.1之前版本

  1. 服务器发送随机字符串 (scramble) 给客户端。
  2. 客户端把用户明文密码加密一下,再将其与服务器发送的随机字符串加密一下,发送给服务端。
  3. 服务端将 mysql.user.password 中的值加上原始随机字符串(scramble)进行加密,如果加密后的值和客户端发送过来的内容一样,则验证成功。

mysql.user.password是明文密码的加密 hash 值; 如果有人知道了这个用户的 password 哈希值,而不用知道原始明文密码,实际上他就能直接登录服务器。

mysql_native_password

4.1 以后版本 数据库中保存的密码是用 SHA1(SHA1(password)) 加密的,其流程为:

  1. 服务器发送随机字符串 (scramble) 给客户端。
  2. 客户端作如下计算,然后客户端将 token 发送给服务端。
1
2
3
4
5
    stage1_hash = SHA1(password)

    token = SHA1(scramble + SHA1(stage1_hash)) XOR stage1_hash
          = SHA1(scramble + SHA1(SHA1(password))) XOR SHA1(password)
          = PASSWORD XOR SHA1(password)
  1. 服务端作如下计算,校验时比对 SHA1(stage1_hash) 和 mysql.user.password 是否相同。
1
2
3
4
    stage1_hash = token XOR SHA1(scramble + mysql.user.password)
                = token XOR PASSWORD
                = [PASSWORD XOR SHA1(password)] XOR PASSWORD
                = SHA1(password)

这里实际用到了异或的自反性: A XOR B XOR B = A ,对于给定的数 A,用同样的运算因子 B 作两次异或运算后仍得到 A 本身

优点是响应机制非常快速,且不需要加密连接。 但是依赖SHA1算法,SHA1已经被证明不太安全。 而且当两个账号密码相同时,mysql_native_password在mysql.user表中转换存储的信息是相同的。

sha256_password

MySQL 5.6开始,支持sha256_password身份验证插件。 在加盐密码上使用了多轮SHA256哈希,以确保哈希转换更加安全。 但是,它需要加密连接或支持RSA密钥对。因此,虽然密码安全性更高,但是安全连接和多轮哈希转换需要在身份验证过程中花费更多时间。

caching_sha2_password

从 MySQL 8.0.3 开始,引入了一个新的身份验证插件caching_sha2_password。 从MySQL 8.0.4开始,MySQL服务器的默认身份验证插件从mysql_native_password更改为caching_sha2_password。 caching_sha2_password 尝试一个两全其美的结合,既解决SHA1安全性问题又解决多轮哈希性能问题。 1、完整验证方式

  1. 客户端连接服务端
  2. 服务端给客户端发送 Nonce(20 字节长的随机数据)
  3. 客户端使用 XOR(SHA256(password), SHA256(SHA256(SHA256(password)), nonce)) 生成 Scramble 发送给服务端
  4. 服务端检查 username/SHA256(SHA256(user_password)) 是否在内存缓存条目中存在,不存在则发送 perform_full_authentication 包到客户端继续认证
  5. 客户端收到 perform_full_authentication 包,可以进行如下处理 5.1 如果连接已经建立基于 SSL 的安全通道,则可以直接发送明文密码到服务端 5.2 向服务端发起获取公钥的请求(或者指定服务端公钥文件),使用公钥+Nonce加密密码,发送加密后的密码到服务端
  6. 服务器通过 SHA256 算法计算得到哈希值,判断是否用户认证通过,通过则发送 OK 包到客户端
  7. 进入命令阶段

2、快速验证方式

  1. 客户端连接服务端
  2. 服务端给客户端发送 Nonce(20 字节长的随机数据)
  3. 客户端使用 XOR(SHA256(password), SHA256(SHA256(SHA256(password)), nonce)) 生成 Scramble 发送给服务端
  4. 服务端检查 username/SHA256(SHA256(user_password)) 是否在内存缓存条目中存在,存在则证明合法;发送 fast_auth_success 包到客户端
  5. 服务端发送 OK 包到客户端
  6. 进入命令阶段

当用户密码变更或者重命名时将删除缓存的密码

  1. 更改用户密码后,将删除所有此类用户的缓存密码哈希。可以通过ALTER USER / SET PASSWORD / GRANT更改密码。
  2. 当使用RENAME USER重命名用户时,其密码哈希的缓存条目将从内存中删除。
  3. 当执行FLUSH PRIVILEGES时,将删除所有缓存的密码哈希。

auth-switch

MySQL的默认身份验证插件修改,但是旧的验证方式依然可用,MySQL会通过auth-switch协议通知客户端切换至当前账号所需要的身份验证插件。

参考