直接查mysql.user表能定位绝大多数未授权后台管理账号,但必须结合host、account_locked、authentication_string和角色链三重验证,否则会漏掉8.0+的高危继承账号;需重点排查空用户名、host为'%'或空密码的账号,并连查mysql.role_edges和mysql.proxies_priv防绕过。

直接查 mysql.user 表就能定位绝大多数未授权后台管理账号,但必须结合 host、account_locked、authentication_string 和角色链三重验证,否则会漏掉 8.0+ 的高危继承账号。
查空用户名、任意主机和空密码这三类最危险账号
它们是未授权访问的“默认入口”,不依赖密码或复杂配置就能被利用。执行以下语句快速暴露:
SELECT user, host, authentication_string, account_locked, password_last_changed
FROM mysql.user
WHERE user = ''
OR host IN ('%', '0.0.0.0', '127.0.0.1')
OR authentication_string IN ('', '*')
OR (authentication_string IS NOT NULL AND LENGTH(authentication_string)
-
user = ''是匿名用户,MySQL 允许无用户名连接(尤其在旧版本中) -
host = '%'不等于“允许所有IP”——它匹配所有未被更精确规则覆盖的来源,极易被扫描器命中 -
authentication_string = ''或长度远小于 40(SHA256 哈希标准长度),说明密码为空或被降级为弱加密插件(如mysql_native_password) - 别只看
account_locked = 'Y'就放心:锁住≠删除,后续ALTER USER ... ACCOUNT UNLOCK一行就恢复
用 SHOW GRANTS FOR 验证每个可疑账号的真实权限边界
SHOW GRANTS 是唯一能反映“当前账号实际能做什么”的命令,mysql.user 里的字段只是静态快照,且在 8.0+ 中对角色权限完全失真。
- 必须写全
'username'@'host',漏掉@'host'会报错ERROR 1141,不是权限为空,是查错了对象 - 重点关注输出中是否含:
GRANT OPTION(可转授)、ALL PRIVILEGES ON *.*(全局控制)、REPLICATION SLAVE(可能用于数据窃取) - 如果返回空或只有
USAGE,不代表安全——该账号可能通过角色获得权限,需继续查mysql.role_edges - 执行
SHOW GRANTS FOR 'admin'@'%'后没看到SUPER?正常。MySQL 压根不显示角色继承来的SUPER,必须实操验证
必须连查 mysql.role_edges 和 mysql.proxies_priv 防绕过
只扫 mysql.user 在 MySQL 8.0+ 环境下等同于“关灯找钥匙”。角色和代理机制让权限可以层层嵌套、动态激活,审计必须穿透两层。
- 查谁绑了角色:
SELECT FROM_USER, FROM_HOST, TO_ROLE FROM mysql.role_edges;—— 每一条都代表一个潜在权限通道 - 对每个
TO_ROLE执行:SHOW GRANTS FOR 'role_name'@'%';(注意主机名不能省,否则报错) - 若角色含
GRANT SUPER ON *.*,再反查哪些用户被授予此角色:SELECT User, Host FROM mysql.user JOIN mysql.role_edges USING(User, Host) WHERE TO_ROLE = 'xxx'; - 别漏
mysql.proxies_priv:SELECT * FROM mysql.proxies_priv WHERE Proxied_host != '' AND With_grant = 'Y';—— 这意味着某个账号能以其他用户身份登录并提权
为什么 CURRENT_USER() 和登录日志不能当审计依据
它们反映的是“此刻谁连上了”,不是“系统里有没有不该存在的账号”。很多后门账号常年不登录,但只要存在,就构成配置风险。
-
CURRENT_USER()返回的是认证后的身份,比如'proxy_user'@'localhost',但它背后可能是'root'@'%'被代理,你查不到 root - 登录失败日志(
mysqld.log或performance_schema.host_cache)只能告诉你“谁试过连”,不能告诉你“谁本就可以连” - 真正要盯的是静态配置残留:比如一个
'bak_admin'@'%'账号,三年没登录记录,account_locked = 'N',password_last_changed是 NULL——它没被用,但它随时能被用 - 定期巡检脚本里如果只 grep
unauthenticated user或统计活跃连接,会彻底忽略这类“静默高危账号”
最易被忽略的一点:刚执行 GRANT SUPER ON *.* TO 'u'@'h' 后,mysql.user.Super_priv 字段可能仍是 'N',因为权限已生效但元数据未刷新;此时 SHOW GRANTS 也看不到,唯一办法是用该账号登录后执行 KILL QUERY 1 实测——权限检查不是读表游戏,是操作验证。











