最小权限需人工逐条确认账号的host、密码策略、权限粒度、连接方式四要素;CREATE USER必须显式执行并指定SSL、密码过期等策略;host须精确匹配连接方式;禁用.授权,只授具体表权限。

直接结论:最小权限不是配完 GRANT 就完事,而是必须人工逐条确认账号的 host、密码策略、权限粒度、连接方式四要素;漏掉任意一项,配置就形同虚设。
CREATE USER 必须显式执行,禁用 GRANT 隐式建户
MySQL 8.0+ 已弃用 GRANT 语句自动创建用户的机制,强行使用会触发 ERROR 1410 (42000)。更危险的是,隐式建户完全跳过密码强度、SSL 要求、过期策略等关键安全项。
- 正确写法:
CREATE USER 'app_user'@'10.20.30.5' IDENTIFIED BY 'StrongPass!2026' REQUIRE SSL PASSWORD EXPIRE INTERVAL 90 DAY; - 必须显式指定
host——'app_user'@'localhost'和'app_user'@'127.0.0.1'是两个独立账号,应用连的是 TCP 还是 socket,决定了权限是否生效 - 别忘了清理残留:
DROP USER IF EXISTS ''@'localhost';,空用户名是常见后门入口
GRANT 必须精确到 database.table,禁用 *.*
写 GRANT SELECT ON app_db.* TO ... 看似合理,但一旦业务库混入 sys_config 或调试表,敏感数据就全暴露了;更糟的是,它默认允许读 information_schema,开发能直接查出所有库名和表结构。
- 真实场景只授具体表:
GRANT SELECT, INSERT, UPDATE ON app_db.orders TO 'app_user'@'10.20.30.5'; - 老版本(REVOKE SELECT ON information_schema.* FROM 'app_user'@'10.20.30.5';(需 SUPER 权限)
- MySQL 8.0.29+ 可先
REVOKE SELECT ON *.* FROM ...,再单独GRANT业务表,更干净 - 绝不给
DROP、ALTER、GRANT OPTION——ALTER本质含隐式DROP,无法拆分,结构变更必须走审批流程
角色(Role)不是银弹,必须设默认且激活
用 CREATE ROLE 管理权限确实省事,但角色默认不激活,新连接登录后仍是空权限状态,应用会报错 ERROR 1044 (42000): Access denied。
- 建角色后必须绑定用户:
GRANT 'order_rw' TO 'app_user'@'10.20.30.5'; - 设为默认角色才能免手动激活:
SET DEFAULT ROLE 'order_rw' TO 'app_user'@'10.20.30.5'; - 角色本身也要遵循最小原则——
CREATE ROLE 'order_rw'后,只GRANT必需权限,别图省事GRANT ALL ON ...
权限变更对已存在连接不生效
FLUSH PRIVILEGES 不是万能同步开关。执行 GRANT 或 REVOKE 后,新连接立即生效;但正在跑的应用连接不会丢权限,也不会突然获得新权限——它维持旧状态直到断连重连。
- 权限回收后,必须配合应用侧操作:重启连接池、滚动更新服务、或等待连接自然超时释放
-
FLUSH PRIVILEGES仅在你绕过GRANT直接UPDATE mysql.user后才必需;8.0+ 大多数情况自动刷新,但保留一句更稳妥 - 上线前务必手动验证:
mysql -u app_user -p -h db-prod.internal -D app_db,然后试DROP TABLE orders;,应明确报错ERROR 1142 (42000)
最容易被忽略的点:账号安全性不只取决于 GRANT 写了什么,而取决于 CREATE USER 时有没有锁死 host、REQUIRE SSL、PASSWORD EXPIRE,以及应用连接字符串里是不是真用了这个账号——而不是悄悄写着 root 或硬编码密码。


















