grant option 是允许用户转授自身所有权限的高危权限,易导致权限扩散失控;应创建时禁用、误授后立即回收,并优先使用角色机制隔离权限。

GRANT OPTION 是什么,为什么它危险
MySQL 的 GRANT OPTION 权限不是“能执行 GRANT 语句”这么简单,而是允许用户把**自己拥有的任何权限**(包括后续别人再授给他的)转授出去。一旦开启,普通账号就能变成“二级管理员”,甚至绕过 DBA 的权限收敛策略。
常见错误现象:Access denied; you need (at least one of) the GRANT OPTION privilege(s) for this operation 这类报错往往出现在你试图用非 root 账号做授权时——但更危险的是,有人顺手加了 WITH GRANT OPTION 却没意识到后果。
创建用户时直接禁用 GRANT OPTION
最稳妥的做法,是在第一次授权时就不给。MySQL 默认不授予 GRANT OPTION,但很多人写授权语句时不注意,默认加上了。
- 正确写法:
GRANT SELECT, INSERT ON mydb.* TO 'appuser'@'10.20.%';(结尾无WITH GRANT OPTION) - 错误写法:
GRANT SELECT ON mydb.* TO 'appuser'@'10.20.%' WITH GRANT OPTION;(哪怕只授一个权限,也打开了扩散门) - 如果用户已存在且误授了,先回收:
REVOKE GRANT OPTION ON *.* FROM 'appuser'@'10.20.%'; -
FLUSH PRIVILEGES;不是必须的(8.0+ 元数据表实时生效),但保险起见可执行
检查现有用户是否意外持有 GRANT OPTION
别假设“没人乱加”,生产库常因历史操作或自动化脚本埋雷。重点查两类范围:
- 全局级:
SELECT User, Host FROM mysql.user WHERE Grant_priv = 'Y'; - 数据库/表级:
SELECT User, Host, Db, Table_name FROM mysql.db WHERE Grant_priv = 'Y';(注意:8.0 中mysql.db表仍有效,但权限逻辑已转向mysql.role_edges和mysql.proxies_priv) - 如果发现不该有的
Y,立刻REVOKE GRANT OPTION,不要等出事
用角色(Role)替代直接授权,天然隔离 GRANT OPTION
MySQL 8.0+ 支持角色,是管理权限扩散最干净的方式:角色本身不能被授予 GRANT OPTION,且用户获得角色权限后,无法将角色再转授出去(除非显式给该用户 GRANT OPTION,但这时目标明确、易审计)。
- 建角色:
CREATE ROLE 'app_reader'; - 授予权限给角色:
GRANT SELECT ON mydb.* TO 'app_reader';(不带WITH GRANT OPTION) - 分配角色给用户:
GRANT 'app_reader' TO 'appuser'@'10.20.%'; - 用户登录后需
SET ROLE 'app_reader';才能生效(或设为默认角色)
角色机制下,GRANT OPTION 只可能出现在“赋予角色”这个动作上(即 GRANT 'role' TO ...),而这个动作本身不需要 GRANT OPTION 权限——所以扩散链从根上就断了。
真正容易被忽略的是:旧版本 MySQL(5.7 及以下)根本不支持角色,升级前得先确认兼容性;另外,应用连接池若未配置 init_connect 或自动 SET ROLE,会导致权限不生效——这不是 bug,是角色设计使然。











