新建应用用户必须先执行revoke all privileges on .,再显式授予业务库权限,禁用系统库访问;grant usage on .作为安全起点允许认证连接但无操作权限;授权后需用新连接验证show grants,避免旧连接缓存导致误判。

新建应用用户前必须先收回全局权限
应用账号默认不该看到任何系统元数据——这不是“限制”,而是基线安全要求。MySQL 的 information_schema 虽然只读,但会泄露表结构、列名、索引甚至 TABLE_ROWS 这类统计信息;mysql 库直接暴露用户、权限、角色等敏感配置;performance_schema 若开放 SELECT,可能被用于监控锁、查询执行计划甚至内存分配。
常见错误是直接 GRANT SELECT ON *.* 或先建用户再慢慢收权,结果因权限叠加导致系统库仍可访问。正确做法是:在 CREATE USER 后、任何 GRANT 前,立即执行:
REVOKE ALL PRIVILEGES ON *.* FROM 'app_user'@'172.16.0.%';
这一步能切断历史脚本残留或继承权限的路径,确保后续授权从零开始。
显式授权时绝对不要提系统库名
只要 GRANT 语句里没出现 mysql、performance_schema、information_schema,就等于完全禁止访问——MySQL 不支持对这些库做表级授权,也不该授任何权限。
- ✅ 正确:
GRANT SELECT, INSERT ON app_db.orders TO 'app_user'@'172.16.0.%'; - ❌ 错误:
GRANT SELECT ON mysql.user TO 'app_user'@'%';(语法虽通,但绝不能执行) - ❌ 隐患:
GRANT SELECT ON *.* TO ...或GRANT SELECT ON app_db.* TO ...后未检查是否误授了系统库
验证是否误授:运行 SELECT User, Host, Db FROM mysql.db WHERE Db IN ('mysql', 'performance_schema', 'information_schema');,若有返回,立刻 REVOKE 或 DROP USER。
为什么 GRANT USAGE ON *.* 是安全起点
GRANT USAGE ON *.* 在 MySQL 8.0+ 中不是摆设,而是“零权限连接占位符”:它允许用户通过认证建立连接,但禁止任何操作。这对应用账号初始化极关键——先建用户、设密码、绑 IP,再单独授权业务库,中间不会出现“已连上却权限过大”的空窗期。
常见错误现象:
- 用
GRANT SELECT ON app_db.* TO 'app'@'%'创建用户时,MySQL 8.0+ 报错ERROR 1410 (42000): You are not allowed to create a user with GRANT——因为没提前CREATE USER - 直接
GRANT ALL ON *.*再REVOKE,结果忘了精准匹配范围,权限实际仍生效(MySQL 权限是叠加的)
标准流程应为:
CREATE USER 'app_user'@'172.16.0.%' IDENTIFIED BY 'strong_pwd' REQUIRE SSL;<br>GRANT USAGE ON *.* TO 'app_user'@'172.16.0.%';<br>GRANT SELECT, INSERT ON app_db.users TO 'app_user'@'172.16.0.%';
连接池和旧连接会缓存旧权限
即使你已执行 GRANT 和 FLUSH PRIVILEGES,应用仍可能报权限错误——问题常出在客户端侧。
原因有三:
- 已存在的连接不会自动更新权限,需手动
KILL对应线程,或让应用重连 - 某些连接池(如 PyMySQL、mysql-connector-java)复用连接时,不重新校验权限,尤其在使用
mysql_native_password认证插件时 - 授权时写的
'app_user'@'172.16.0.%'和实际连接用的 host 不一致(比如 DNS 解析后变成'app_user'@'host.example.com'),导致权限记录未命中
验证是否生效,必须用新连接登录后执行 SHOW GRANTS FOR 'app_user'@'172.16.0.%';,确认输出里没有 ON mysql.* 或类似宽泛条目。











