MySQL 8.0 无法直接禁止用户改自己密码,因无PASSWORD_CHANGE_PRIV等细粒度权限;唯一有效方式是同步执行三步:回收CREATE USER及mysql.*写权限、执行ACCOUNT LOCK锁定账户、清理应用中硬编码的明文密码。

MySQL 8.0 无法直接禁止用户修改自己密码——没有 PASSWORD_CHANGE_PRIV 这类权限,也没有 DENY ALTER USER 语法。所谓“限制”,只能靠切断能力源头 + 拦截执行路径实现。
回收 CREATE USER 权限是第一步,但容易漏掉隐式继承
用户能改自己密码,根本原因不是有 ALTER USER 权限(这甚至不是合法权限名),而是拥有 CREATE USER 权限——它隐含修改自身认证信息的能力。
-
REVOKE CREATE USER ON *.* FROM 'u'@'h'必须执行,且需确认该用户未通过角色获得该权限:查SELECT * FROM mysql.role_edges WHERE TO_HOST = 'h' AND TO_USER = 'u',如有,得对对应角色也REVOKE CREATE USER - 单纯
REVOKE USAGE无效,因为USAGE是空权限,不能被回收 - 权限变更后必须重连验证:
SHOW GRANTS FOR 'u'@'h'应不再显示CREATE USER或任何涉及mysql.*的写权限
ACCOUNT LOCK 比改密码更彻底,但不等于禁用所有连接
ALTER USER 'u'@'h' ACCOUNT LOCK 会让后续登录直接报错 ERROR 3118 (HY000): Access denied for user 'u'@'h'. Account is locked.,但它不阻止已建立的长连接继续执行命令。
- 锁账户后,用户仍可能在连接未断开时执行
ALTER USER ... IDENTIFIED BY—— 所以必须先收权限,再锁账户 - 如果应用使用连接池(如 HikariCP),要确认其未启用自动重连;否则锁账户后,池子会持续尝试重连并暴露错误细节
- 锁账户不影响其他用户,也不影响 root 管理操作,适合临时封禁或程序账号停用
validate_password 插件只校验,不禁止;设错参数等于没设
插件不会阻止语句执行,而是在 ALTER USER、SET PASSWORD 时校验新密码,不满足策略就抛 ERROR 1819。但它对已有弱密码完全无感。
- 必须启用插件:
INSTALL PLUGIN validate_password SONAME 'validate_password.so'(Linux)或对应 DLL(Windows) -
SET PERSIST validate_password_policy = MEDIUM后,还需同步调SET PERSIST validate_password_length = 8;否则默认仍要求 8 位,单独设策略没用 - 插件无法防绕过:若用户仍有
UPDATE ON mysql.user权限,可直写authentication_string字段——所以权限回收仍是前提
真正卡死,靠的是三件事同时生效
少一步,用户就可能绕过。比如只锁账户但没回收权限,用户用已有连接就能改密;只收权限但没清理外部硬编码,应用仍可用旧凭据连上并执行改密语句。
- 检查应用配置(
application.yml、.env)是否存了该用户的明文密码——这是最常被忽略的后门 - 确认连接池未缓存旧凭证,且驱动版本支持
ACCOUNT LOCK响应(5.7+ 基本都支持) - 如果是程序账号,建议改用角色管理:
CREATE ROLE 'app_reader'; GRANT SELECT ON db.* TO 'app_reader'; GRANT 'app_reader' TO 'u'@'h';,后续调权限只需动角色
最终效果不是“用户输不了 ALTER USER”,而是“输完执行时报错或连不上”。生产环境里,账户锁定、权限回收、外部凭证清理这三件事必须同步做,缺一不可。


















