直接grant all on .极危险,因赋予drop database、shutdown等高危权限,应用账号亦可删库或重启实例;应遵循最小权限原则,按实际sql需求精确授权,禁用grant option,优先细化到表或列,并利用mysql 8.0角色与动态权限机制实现安全管控。
为什么直接 GRANT ALL ON *.* 很危险
因为这会让用户拥有 DROP DATABASE、SHUTDOWN、RELOAD 等高危权限,哪怕只是应用连接账号,也能意外删库或重启实例。MySQL 8.0 默认启用 sql_mode=STRICT_TRANS_TABLES,但权限宽松本身不触发任何警告,风险完全靠人控制。
最小化不是“能连上就行”,而是“只给当前业务 SQL 真正需要的权限”。比如一个只查订单列表的接口,连 INSERT 都不该有。
- 应用账号绝不分配
GRANT OPTION—— 否则可能自行提权 - 避免使用
ON *.*或ON database.*,优先细化到表甚至列(如SELECT(order_id, status) ON db.orders) - MySQL 8.0+ 支持角色(
CREATE ROLE),可用角色封装权限组合,再授给用户,便于批量调整
如何用 SHOW GRANTS 定位冗余权限
SHOW GRANTS FOR 'app_user'@'10.20.%' 返回的是当前生效权限集合,但注意:它不区分权限来源(是直授还是通过角色继承),也不显示已撤销但未刷新的缓存。真正要确认最小集,得结合实际运行日志反推。
推荐做法是开启 general_log(仅临时),让应用跑完典型链路(登录 → 查询 → 提交),再 grep 出所有 SELECT/UPDATE 语句涉及的库、表、字段:
SELECT DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(argument, ' ', 3), ' ', -1) AS table_name, SUBSTRING_INDEX(argument, ' ', 2) AS stmt_type FROM mysql.general_log WHERE argument LIKE 'SELECT %' OR argument LIKE 'UPDATE %' AND argument NOT LIKE '%information_schema%' AND event_time > NOW() - INTERVAL 5 MINUTE;
结果出来后,再用 GRANT SELECT(col1,col2) ON db.table TO ... 精确授权,比凭经验猜更可靠。
MySQL 8.0 的动态权限与旧版兼容陷阱
MySQL 8.0 把部分管理权限(如 BACKUP_ADMIN、CLONE_ADMIN)从全局权限中拆出,变成可独立授予的「动态权限」。它们不会出现在 SHOW GRANTS 的传统输出里,必须用 SHOW GRANTS FOR 'u'@'h' USING role_name 或查 mysql.role_edges 才能看到。