Skip to content

MySQL 密码策略与管理

密码验证插件

安装和启用密码验证插件

MySQL 5.7 及之前版本

sql
-- 安装validate_password插件
INSTALL PLUGIN validate_password SONAME 'validate_password.so';

-- 查看插件状态
SHOW PLUGINS LIKE 'validate_password';

-- 查看插件变量
SHOW VARIABLES LIKE 'validate_password%';

MySQL 8.0 及之后版本

sql
-- 安装validate_password组件
INSTALL COMPONENT 'file://component_validate_password';

-- 查看组件状态
SELECT * FROM mysql.component;

-- 查看组件变量
SHOW VARIABLES LIKE 'validate_password%';

密码策略配置

密码复杂度要求

validate_password 插件参数

参数名称说明默认值
validate_password_length密码最小长度8
validate_password_number_count密码中至少包含的数字个数1
validate_password_special_char_count密码中至少包含的特殊字符个数1
validate_password_mixed_case_count密码中至少包含的大小写字母个数1
validate_password_policy密码策略级别(0=LOW, 1=MEDIUM, 2=STRONG)1
validate_password_dictionary_file密码字典文件路径

密码策略级别详解

策略级别说明
LOW只检查密码长度
MEDIUM检查密码长度、数字、大小写字母和特殊字符
STRONG检查密码长度、数字、大小写字母、特殊字符和字典文件

配置密码策略

临时配置

sql
-- 设置密码最小长度为12
SET GLOBAL validate_password_length = 12;

-- 设置密码策略为STRONG
SET GLOBAL validate_password_policy = 2;

-- 设置密码字典文件
SET GLOBAL validate_password_dictionary_file = '/path/to/dictionary.txt';

永久配置

在MySQL配置文件中添加以下参数:

ini
[mysqld]
# 密码验证插件配置
validate_password_length = 12
validate_password_number_count = 2
validate_password_special_char_count = 1
validate_password_mixed_case_count = 2
validate_password_policy = STRONG
validate_password_dictionary_file = /path/to/dictionary.txt

密码生命周期管理

密码过期策略

配置全局密码过期时间

sql
-- 设置全局密码过期时间为90天
SET GLOBAL default_password_lifetime = 90;

-- 禁用密码过期
SET GLOBAL default_password_lifetime = 0;

在配置文件中添加:

ini
[mysqld]
default_password_lifetime = 90

为特定用户设置密码过期时间

sql
-- 创建用户时设置密码过期时间为60天
CREATE USER 'user'@'host' IDENTIFIED BY 'password' PASSWORD EXPIRE INTERVAL 60 DAY;

-- 修改现有用户的密码过期时间
ALTER USER 'user'@'host' PASSWORD EXPIRE INTERVAL 60 DAY;

-- 禁用特定用户的密码过期
ALTER USER 'user'@'host' PASSWORD EXPIRE NEVER;

-- 遵循全局密码过期策略
ALTER USER 'user'@'host' PASSWORD EXPIRE DEFAULT;

密码历史记录

启用密码历史记录

sql
-- 设置密码历史记录为5(用户不能重复使用最近5个密码)
SET GLOBAL password_history = 5;

-- 设置密码重用间隔为365天
SET GLOBAL password_reuse_interval = 365;

在配置文件中添加:

ini
[mysqld]
password_history = 5
password_reuse_interval = 365

为特定用户设置密码历史

sql
-- 创建用户时设置密码历史记录为10
CREATE USER 'user'@'host' IDENTIFIED BY 'password' PASSWORD HISTORY 10;

-- 修改现有用户的密码历史记录
ALTER USER 'user'@'host' PASSWORD HISTORY 10;

用户密码管理

创建用户时的密码设置

sql
-- 创建用户并设置密码(自动哈希)
CREATE USER 'user'@'host' IDENTIFIED BY 'StrongPassword123!';

-- 使用特定认证插件创建用户
CREATE USER 'user'@'host' IDENTIFIED WITH mysql_native_password BY 'StrongPassword123!';
CREATE USER 'user'@'host' IDENTIFIED WITH caching_sha2_password BY 'StrongPassword123!';

修改用户密码

sql
-- 使用ALTER USER修改密码(推荐)
ALTER USER 'user'@'host' IDENTIFIED BY 'NewStrongPassword456!';

-- 使用SET PASSWORD修改密码(不推荐,MySQL 8.0中已弃用部分语法)
SET PASSWORD FOR 'user'@'host' = 'NewStrongPassword456!';

重置密码

忘记root密码时的重置方法

  1. 停止MySQL服务

    bash
    systemctl stop mysqld
  2. 以跳过授权表的方式启动MySQL

    bash
    mysqld_safe --skip-grant-tables --skip-networking &
  3. 登录MySQL并修改密码

    sql
    -- 登录MySQL
    mysql -u root
    
    -- 切换到mysql数据库
    USE mysql;
    
    -- 修改root密码
    UPDATE user SET authentication_string = PASSWORD('NewRootPassword123!') WHERE User = 'root' AND Host = 'localhost';
    
    -- MySQL 8.0使用以下命令
    ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewRootPassword123!';
    
    -- 刷新权限
    FLUSH PRIVILEGES;
    
    -- 退出MySQL
    EXIT;
  4. 重启MySQL服务

    bash
    systemctl restart mysqld

密码过期处理

sql
-- 手动将用户密码设置为过期
ALTER USER 'user'@'host' PASSWORD EXPIRE;

-- 查看密码过期状态
SELECT user, host, password_expired FROM mysql.user;

-- 用户登录时需要修改密码
mysql -u user -p
Enter password: 
ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.

-- 修改过期密码
ALTER USER USER() IDENTIFIED BY 'NewPassword123!';

密码安全最佳实践

密码策略最佳实践

  1. 设置强密码策略

    • 密码长度至少12个字符
    • 要求包含大小写字母、数字和特殊字符
    • 使用STRONG密码策略级别
    • 配置密码字典文件,防止使用常见密码
  2. 启用密码生命周期管理

    • 设置密码过期时间(建议90-180天)
    • 启用密码历史记录,防止密码重用
    • 要求用户定期更换密码
  3. 使用安全的认证插件

    • MySQL 8.0默认使用caching_sha2_password,更安全
    • 避免使用mysql_old_password等不安全的认证插件
    • 考虑使用外部认证插件(如LDAP、PAM)

用户管理最佳实践

  1. 最小权限原则

    • 只为用户授予必要的权限
    • 避免使用GRANT ALL PRIVILEGES
    • 定期审核用户权限
  2. 用户命名规范

    • 使用有意义的用户名,避免使用通用名称
    • 包含用户角色或职责信息
    • 例如:app_read@'%'admin_user@'localhost'
  3. 主机限制

    • 限制用户的连接主机
    • 避免使用user@'%'(允许从任何主机连接)
    • 例如:user@'192.168.1.0/24'user@'localhost'
  4. 定期清理无用用户

    • 删除不再使用的用户
    • 禁用长期不活跃的用户
    • 示例:
      sql
      -- 删除用户
      DROP USER 'user'@'host';
      
      -- 禁用用户
      UPDATE mysql.user SET account_locked = 'Y' WHERE user = 'user' AND host = 'host';

密码存储安全

  1. 避免明文存储密码

    • 永远不要在配置文件、脚本或代码中明文存储密码
    • 使用环境变量或密钥管理系统存储密码
    • 示例:
      bash
      # 使用环境变量
      export MYSQL_ROOT_PASSWORD='StrongPassword123!'
      mysql -u root -p$MYSQL_ROOT_PASSWORD
  2. 加密存储密码文件

    • 如果必须存储密码文件,使用加密方式
    • 限制密码文件的访问权限
    • 示例:
      bash
      # 设置密码文件权限
      chmod 600 ~/.my.cnf
      
      # 密码文件内容
      [client]
      user = root
      password = StrongPassword123!
  3. 使用SSL/TLS加密连接

    • 启用SSL/TLS加密,防止密码在传输过程中被窃取
    • 配置require_secure_transport = ON强制使用加密连接

密码审计与监控

审计密码策略合规性

sql
-- 查看所有用户的密码策略相关信息
SELECT 
  user, 
  host, 
  plugin, 
  password_expired, 
  password_last_changed, 
  password_lifetime 
FROM mysql.user;

-- 查找使用弱密码的用户
SELECT user, host FROM mysql.user WHERE authentication_string = PASSWORD('123456');

-- 查找密码即将过期的用户(30天内)
SELECT 
  user, 
  host, 
  password_last_changed, 
  password_lifetime 
FROM mysql.user 
WHERE password_lifetime > 0 
  AND DATEDIFF(NOW(), password_last_changed) >= (password_lifetime - 30);

监控密码相关事件

  1. 启用审计日志

    ini
    [mysqld]
    plugin-load-add=audit_log.so
    audit_log_format=JSON
    audit_log_policy=ALL
  2. 监控密码修改事件

    • 使用Performance Schema监控密码相关语句
    • 示例:
      sql
      -- 启用语句事件监控
      UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_statements_history_long';
      
      -- 查看密码修改语句
      SELECT * FROM performance_schema.events_statements_history_long 
      WHERE SQL_TEXT LIKE '%ALTER USER%IDENTIFIED BY%' OR SQL_TEXT LIKE '%SET PASSWORD%';

常见问题(FAQ)

Q1: 如何禁用密码验证插件?

A1: 可以通过以下方法禁用密码验证插件:

sql
-- MySQL 5.7及之前版本
UNINSTALL PLUGIN validate_password;

-- MySQL 8.0及之后版本
UNINSTALL COMPONENT 'file://component_validate_password';

或者在配置文件中添加:

ini
[mysqld]
validate_password = OFF

Q2: 如何处理应用程序无法满足强密码策略的情况?

A2: 如果应用程序无法生成符合强密码策略的密码,可以考虑以下方案:

  1. 调整密码策略为适合应用程序的级别
  2. 为应用程序用户创建专门的密码策略
  3. 考虑使用应用层密码管理,在应用层处理密码复杂度
  4. 使用API密钥或证书认证替代密码认证

Q3: MySQL 8.0 中的 caching_sha2_passwordmysql_native_password 有什么区别?

A3: 主要区别:

特性caching_sha2_passwordmysql_native_password
安全性更高,使用SHA-256算法较低,使用SHA-1算法
性能更好,支持缓存一般
默认使用MySQL 8.0默认MySQL 5.7默认
客户端支持需要较新的客户端广泛支持

Q4: 如何批量修改用户密码?

A4: 可以使用以下方法批量修改用户密码:

sql
-- 创建存储过程批量修改密码
DELIMITER //
CREATE PROCEDURE batch_update_password()
BEGIN
  DECLARE done INT DEFAULT FALSE;
  DECLARE user_name VARCHAR(100);
  DECLARE host_name VARCHAR(100);
  DECLARE cur CURSOR FOR SELECT user, host FROM mysql.user WHERE user NOT IN ('root', 'mysql.sys', 'mysql.session', 'mysql.infoschema');
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
  
  OPEN cur;
  
  read_loop: LOOP
    FETCH cur INTO user_name, host_name;
    IF done THEN
      LEAVE read_loop;
    END IF;
    
    -- 生成随机密码(实际使用中应使用更安全的随机密码生成方式)
    SET @new_password = CONCAT('NewPass_', FLOOR(RAND() * 1000000));
    SET @sql = CONCAT('ALTER USER ''', user_name, '''@''', host_name, ''' IDENTIFIED BY ''', @new_password, ''';');
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
    
    -- 记录新密码(实际使用中应安全存储)
    SELECT CONCAT('User: ', user_name, '@', host_name, ', New Password: ', @new_password) AS result;
  END LOOP;
  
  CLOSE cur;
END //
DELIMITER ;

-- 调用存储过程
CALL batch_update_password();

Q5: 如何防止暴力破解密码?

A5: 防止暴力破解密码的措施:

  1. 启用密码失败次数限制

    sql
    -- 设置连续失败5次锁定账户
    SET GLOBAL max_connect_errors = 5;
  2. 使用防火墙限制访问IP

    bash
    # 使用iptables限制访问IP
    iptables -A INPUT -p tcp --dport 3306 -s 192.168.1.0/24 -j ACCEPT
    iptables -A INPUT -p tcp --dport 3306 -j DROP
  3. 启用双因素认证

    • 使用第三方插件(如Google Authenticator)实现双因素认证
  4. 监控登录失败日志

    bash
    # 监控登录失败日志
    grep "Access denied" /var/log/mysqld.log

Q6: 如何迁移用户密码到新的认证插件?

A6: 迁移用户密码到新的认证插件的方法:

sql
-- 将用户从mysql_native_password迁移到caching_sha2_password
ALTER USER 'user'@'host' IDENTIFIED WITH caching_sha2_password BY 'StrongPassword123!';

-- 批量迁移所有用户
SELECT CONCAT('ALTER USER ''', user, '''@''', host, ''' IDENTIFIED WITH caching_sha2_password BY ''', authentication_string, ''';') 
FROM mysql.user 
WHERE plugin = 'mysql_native_password';

Q7: 密码策略对性能有影响吗?

A7: 密码策略对MySQL性能的影响很小,主要在以下场景:

  • 用户创建和密码修改时的密码复杂度检查
  • 使用STRONG策略且配置了字典文件时,会增加密码检查时间

一般情况下,密码策略的性能影响可以忽略不计,建议优先考虑安全性。

Q8: 如何备份和恢复用户密码?

A8: 备份和恢复用户密码的方法:

bash
# 备份mysql.user表
mysqldump -u root -p mysql user > mysql_user_backup.sql

# 恢复mysql.user表
mysql -u root -p mysql < mysql_user_backup.sql

# 刷新权限
mysql -u root -p -e "FLUSH PRIVILEGES;"

注意:

  • 备份和恢复时要确保MySQL版本兼容
  • 恢复后需要刷新权限
  • 考虑使用mysqlpump工具备份用户,它能更好地处理密码哈希