必须显式创建用户、立即回收默认权限、再逐条授予具体库表操作权——跳过任何一步都可能让“最小权限”变成纸面安全。

必须显式创建用户、立即回收默认权限、再逐条授予具体库表操作权——跳过任何一步都可能让“最小权限”变成纸面安全。
CREATE USER 后不立刻 REVOKE ALL PRIVILEGES 就等于没设防
MySQL 新建用户看似只有 USAGE 权限,但老版本或某些部署下会残留对 mysql 或 information_schema 的隐式可读性。更麻烦的是,后续 GRANT 可能叠加出意料之外的能力。
- 先执行
CREATE USER 'app_user'@'10.20.30.%' IDENTIFIED BY 'strong_password_123' REQUIRE SSL PASSWORD EXPIRE INTERVAL 90 DAY; - 紧接着必须执行
REVOKE ALL PRIVILEGES ON *.* FROM 'app_user'@'10.20.30.%';(注意:不是REVOKE ALL,必须带ON *.*) - 漏掉这步,
SHOW GRANTS看起来干净,但应用仍可能查到information_schema.tables或触发权限缓存异常
GRANT 时用 *.* 或 database.* 是最常见也最危险的语法糖
写 GRANT SELECT ON *.* TO 'app_user'@'10.20.30.%'; 等于把整套 MySQL 当裸机交出去;用 app_db.* 虽比 *.* 好,但一旦业务加新表,权限自动生效,违背最小化本意。
- 只授实际用到的表:
GRANT SELECT, INSERT ON `app_db`.`orders` TO 'app_user'@'10.20.30.%'; - 字段级权限值得考虑:
GRANT SELECT (id, name, email) ON `app_db`.`users` TO 'app_user'@'10.20.30.%'; - CI/CD 账号连
flyway_schema_history都要单独授权:GRANT SELECT, INSERT, UPDATE ON `app_db`.`flyway_schema_history` TO 'ci_deploy'@'10.20.30.40';
别信 SHOW GRANTS 显示的结果,元数据访问必须显式禁掉
你看到 SHOW GRANTS FOR 'app_user'@'10.20.30.%'; 只有 SELECT 和 INSERT,但应用照样能查 information_schema.columns——这不是 bug,是 MySQL 默认行为。
- 必须额外执行:
REVOKE SELECT ON information_schema.* FROM 'app_user'@'10.20.30.%'; - 这条语句需要执行者本身有
SUPER权限,普通 DBA 账号可能没这个能力,得提前确认权限链 - MySQL 8.0.29+ 支持
show_compatibility_56=OFF,但不如显式REVOKE可靠
角色(ROLE)在 8.0+ 是更可控的落地方式,但激活逻辑容易被忽略
用 CREATE ROLE + GRANT ... TO role + GRANT role TO user 是生产环境 RBAC 的标准路径,但权限不会自动生效。
- 角色必须显式启用:
SET DEFAULT ROLE 'app_reader' TO 'webapp'@'10.20.30.%'; -
SHOW GRANTS FOR 'webapp'@'10.20.30.%'不显示角色权限,得用SHOW GRANTS FOR 'webapp'@'10.20.30.%' USING 'app_reader'; - 连接池若没配
sessionVariables或connection-init-sql,每次连接后角色仍是未激活状态
真正卡住最小权限落地的,往往不是语法写错,而是忘了 REVOKE SELECT ON information_schema.* 这一行,或者以为 CREATE USER 后权限就是空的——MySQL 的“干净”,从来不是默认状态,而是靠每一步显式动作堆出来的。











