应精确授权到具体表而非通配符,禁用with grant option和'%'主机匹配,mysql 8.0+需注意角色激活与host优先级匹配。

授权时指定具体数据库和表名,别用 * 通配符
MySQL 的 GRANT 语句里写 ON *.* 或 ON mydb.* 看似方便,实则埋雷——前者直接给全实例权限,后者只要库名存在,用户就能创建/删表。真正安全的做法是把资源范围收窄到最小必要粒度。
常见错误现象:GRANT SELECT ON myapp.* TO 'appuser'@'%' —— 这个用户能查 myapp 下任意表,包括未来新建的敏感日志表、配置表。
- 生产环境应明确到表:
GRANT SELECT ON myapp.users TO 'appuser'@'%' - 如果必须跨多张业务表,逐条授权比用
myapp.*更可控 - 注意:MySQL 8.0+ 支持列级权限,敏感字段(如
password_hash)可单独REVOKE SELECT
用 WITH GRANT OPTION 前先问“这个人真需要转授吗”
加了这个选项,被授权者就能把同级甚至更低权限再给别人,相当于在权限体系里开了后门。绝大多数应用账号完全不需要它。
使用场景极少:只有 DBA 团队内部交接、或临时委派某人管理某个子系统时才考虑。
- 默认不加——
GRANT INSERT ON myapp.orders TO 'order_writer'@'10.20.%'就够了 - 加了就必须配套审计:查
mysql.role_edges或定期SHOW GRANTS FOR 'user'@'host' - MySQL 8.0 后更危险:带
WITH GRANT OPTION的角色可能意外继承高权限
避免用 '%' 匹配所有主机,限制连接来源 IP 段
权限本身再细,连进来的人不对也没用。'user'@'%' 允许从任意 IP 连接,等于把锁孔暴露在公网。
性能与安全影响:MySQL 在认证阶段需查 mysql.user 表匹配 User+Host,'%' 会降低匹配效率,且无法利用网络层隔离做二次过滤。
- 内网服务统一用子网:
'appuser'@'10.20.%.%'或更严的'appuser'@'10.20.30.%' - 跳板机或运维账号可用具体 IP:
'dba'@'192.168.5.12' - 绝对不要在生产库开
'root'@'%'—— 这不是权限过大,是放弃防线
执行 FLUSH PRIVILEGES 不是必须的,但改完得确认生效路径
很多人习惯改完就敲 FLUSH PRIVILEGES,其实只要用 GRANT/REVOKE 语句操作,MySQL 会自动重载权限表;只有直接 INSERT/UPDATE mysql.user 才需要手动刷。
容易踩的坑:以为刷了就生效,结果忘了新账号要重新连接——权限检查发生在连接建立时,已存在的连接不会动态更新权限。
- 验证是否生效:用目标账号新起一个连接,执行
SELECT CURRENT_USER(), USER(); - 查实际权限:
SHOW GRANTS FOR 'appuser'@'10.20.30.5';(注意 Host 必须完全匹配) - MySQL 8.0+ 角色权限还要看
ACTIVE ROLES是否启用
最常被忽略的是 Host 匹配的细节:MySQL 会优先匹配最长的 Host 模式,'user'@'10.20.30.5' 比 'user'@'10.20.%' 优先级高,但如果你只授了后者,前者连上来根本拿不到权限——这种错配不会报错,只会静默拒绝。











