应用账号必须显式收回全局权限并验证系统库访问限制,严禁直接GRANT继承隐式权限;需删除mysql.db残留记录、精确匹配Host、检查角色继承,并用真实连接测试USE和SELECT操作是否报错。

应用账号不该有权限看到 mysql、information_schema 或 performance_schema,也不该能 USE 其他业务库——这不是靠“不授权”就能默认安全的,必须显式清理和验证。
先收回全局权限,再授业务库权限
很多事故源于新用户创建后直接 GRANT,结果继承了隐式全局权限或残留配置。MySQL 权限是叠加的,没显式收回,就可能生效。
CREATE USER 'app_user'@'10.20.30.%' IDENTIFIED BY 'strong_pwd';-
REVOKE ALL PRIVILEGES ON *.* FROM 'app_user'@'10.20.30.%';—— 这步不能跳,尤其在复用旧脚本或升级后环境 -
GRANT SELECT, INSERT, UPDATE ON app_db.orders TO 'app_user'@'10.20.30.%';—— 明确到库+表,不用app_db.*除非真需要全库操作 -
FLUSH PRIVILEGES;—— 对REVOKE和直改表操作才必须;GRANT自动刷新,但加这句无害,且能统一行为
检查是否误开了系统库访问
哪怕没主动 GRANT,某些旧版本迁移、复制账户或脚本残留可能导致 mysql.db 表里存在系统库记录,用户仍可访问。
- 运行:
SELECT User, Host, Db FROM mysql.db WHERE Db IN ('mysql', 'performance_schema', 'information_schema'); - 如果有返回,立刻执行:
DELETE FROM mysql.db WHERE User='app_user' AND Host='10.20.30.%' AND Db IN ('mysql', 'performance_schema', 'information_schema'); - 再执行一次
FLUSH PRIVILEGES; - MySQL 8.0.23+ 可用
REVOKE SELECT ON mysql.* FROM 'app_user'@'10.20.30.%';,但低版本不支持此语法,必须删表记录
验证权限是否真正受限
SHOW GRANTS FOR 'app_user'@'10.20.30.%' 只看语句,不代表实际行为。必须用该用户真实连接测试。
- 用
mysql -uapp_user -p -h your_host登录 - 执行
SHOW DATABASES;—— 应只列出app_db(或空,取决于 MySQL 版本和show_database_privilege设置) - 尝试
USE mysql;—— 必须报错Access denied for database 'mysql' - 尝试
SELECT * FROM information_schema.TABLES LIMIT 1;—— 同样应报错,而非返回空结果 - 如果 ORM 或连接池缓存了旧权限,重启应用或连接池,否则测试无效
别忽略 host 匹配和角色继承问题
权限生效依赖精确的 User+Host 匹配,而角色(ROLE)可能带来隐式权限叠加,这两点最容易被漏查。
-
'app_user'@'10.20.30.%'和'app_user'@'%'是两个完全独立的账号,权限不共享 - 如果用了角色:
SHOW GRANTS FOR 'app_user'@'10.20.30.%'会显示GRANT 'app_role' TO ...,但不会展开角色内权限;需额外查SELECT * FROM mysql.role_edges WHERE TO_HOST='10.20.30.%' AND TO_USER='app_user'; - 角色权限变更后,用户会话不会自动更新,需重新登录或执行
SET ROLE DEFAULT; - 生产环境禁用
'%',哪怕测试环境也优先用'10.20.30.%'或 DNS 名,避免 IP 段变动时权限失控
最麻烦的不是设错权限,而是设完没验证、改完没清理残留、用了角色却忘了它也会继承权限。上线前花两分钟用真实账号连进去敲几条命令,比事后排查锁表或数据泄露便宜得多。


















