MySQL 8.0+中mysql.user表Priv字段失效,真实权限存于数据字典;应通过SHOW GRANTS、mysql.global_grants及mysql.role_edges+default_roles联合审计全局ALL权限。

查mysql.user表的Priv字段根本不管用
MySQL 8.0 之后,mysql.user 表里的 Select_priv、Insert_priv 等字段已**不再反映真实权限状态**——它们只在初始化或执行 FLUSH PRIVILEGES 后被同步一次,且不支持动态更新。真正生效的是存储在数据字典(mysql.role_edges、mysql.global_grants 等)中的权限元数据。
直接 SELECT * FROM mysql.user WHERE Select_priv = 'Y' 这类查询,结果可能严重滞后甚至完全错误。
- MySQL 5.7 及更早版本:字段仍部分有效,但需配合
SHOW GRANTS FOR 'u'@'h'验证 - MySQL 8.0+:字段仅作兼容保留,
global_grants表才是权威来源 - 如果用户通过角色获得权限,
mysql.user根本不体现,必须查mysql.role_edges+mysql.default_roles
用SHOW GRANTS逐个检查最可靠
这是唯一能拿到当前实际生效权限的方式,不依赖表字段快照,也不绕过权限合并逻辑(比如用户权限 + 角色权限 + 默认角色叠加)。
实操建议:
- 先列出所有非系统账号:
SELECT User, Host FROM mysql.user WHERE User NOT IN ('mysql.infoschema', 'mysql.session', 'mysql.sys', 'root') - 对每个
User@Host执行:SHOW GRANTS FOR 'u'@'h',观察输出是否含GRANT ALL PRIVILEGES ON *.* - 注意区分
ALL PRIVILEGES和ALL:后者是旧语法别名,效果等同;但若只看到SELECT, INSERT, UPDATE等离散权限,就不算全局 ALL - 脚本化检查时,用
mysql -Nse "SHOW GRANTS FOR 'u'@'h'" | grep -q "ON \*.\*"判断是否存在全局范围
mysql.global_grants表能快速定位(仅 MySQL 8.0.16+)
该表直接记录哪些账户拥有 GLOBAL 级别的显式授权,包括 ALL、SUPER、REPLICATION CLIENT 等。
关键点:
- 字段
privilege值为ALL PRIVILEGES(注意带空格和复数),不是ALL - 字段
with_grant_option为Y表示还能授给别人,属于高危配置 - 执行:
SELECT user, host, privilege, with_grant_option FROM mysql.global_grants WHERE privilege = 'ALL PRIVILEGES' - 该表不包含通过角色继承的全局权限——角色本身不会出现在这里,必须额外查
role_edges链路
角色链路不能漏:mysql.role_edges + mysql.default_roles
一个用户没被直接授过 ALL ON *.*,但只要它激活了某个拥有该权限的角色,就等效拥有全局 ALL。
检查步骤:
- 查用户启用的角色:
SELECT * FROM mysql.default_roles WHERE USER = 'u' AND HOST = 'h' - 顺着
role_edges查角色继承关系:SELECT from_user, from_host, to_user, to_host FROM mysql.role_edges WHERE from_user = 'role_name' - 再查该角色本身的全局权限:
SELECT * FROM mysql.global_grants WHERE user = 'role_name' - 注意:角色名在
global_grants中以'role_name'@'%'形式存储,Host固定为%
权限审计真正难的不是查单点,而是把「用户 → 默认角色 → 角色 → 全局权限」这条链路完整串起来。少一环,就可能漏掉一个拥有全局 ALL 的账号。










