MySQL默认允许所有用户读取information_schema和performance_schema,即使仅有某表SELECT权限;需显式回收系统库权限或升级至8.0.29+并配置元数据可见性控制。

只给 SELECT 权限时为什么还是能查到系统表?
MySQL 默认开启 information_schema 和 performance_schema 的全局可读,即使用户只有某张业务表的 SELECT 权限,也能执行 SHOW TABLES 或查询 information_schema.tables。这不是权限配置失效,而是 MySQL 的设计行为。
真正有效的限制方式是:关闭元数据可见性(8.0+)或显式回收:
- MySQL 8.0.29+ 可设
show_compatibility_56=OFF+skip_show_database(已废弃),更可靠的是用REVOKE SELECT ON *.* FROM 'user'@'%',再仅授予具体库表 - 对老版本,必须显式执行
REVOKE SELECT ON information_schema.* FROM 'user'@'%'(需有 SUPER 权限才能 revoke 系统库) - 应用连接时指定
--skip-secure-auth无意义,不解决权限粒度问题
CREATE USER 后立刻 GRANT 会遗漏什么?
直接 CREATE USER 'app'@'10.20.%' IDENTIFIED BY 'pwd'; GRANT SELECT,INSERT ON shop.orders TO 'app'@'10.20.%'; 看似完整,但漏掉三个关键点:
- 没限制连接来源:应加
REQUIRE SSL或REQUIRE X509(若用 TLS) - 没设密码策略:MySQL 8.0 默认启用
validate_password,但新用户不会自动继承,需额外ALTER USER ... PASSWORD EXPIRE INTERVAL 90 DAY - 没禁用匿名用户:残留的
''@'localhost'可能被绕过,务必运行DROP USER IF EXISTS ''@'localhost';
如何让开发不能删表却又能改表结构?
MySQL 的 ALTER 权限本身包含 DROP 能力(如 ALTER TABLE ... RENAME TO 实质是 drop+create),无法靠标准权限分离。可行路径只有两条:
- 用 MySQL 8.0 的角色(ROLE)机制,把
ALTER和DROP拆到不同角色,再按需分配——但注意:角色不能限制单条语句,只能控制权限集合 - 更实际的做法是禁用直接 DDL,改用运维平台审批流程;数据库层只给
SELECT, INSERT, UPDATE, DELETE,结构变更统一走mysqlpump导出 + 审核 SQL + DBA 执行 - 误操作防护:开启
sql_log_bin=OFF仅对当前会话无效,不能防误删;真正有效的是设置read_only=ON(主库除外)+super_read_only=ON(8.0.11+)
GRANT ALL PRIVILEGES ON *.* 是不是最危险的操作?
是,但更危险的是 GRANT ... WITH GRANT OPTION。前者最多让账号拥有全部权限,后者允许它再把权限转授出去,形成权限扩散链。
常见误操作场景:
- 给监控账号(如 Prometheus 的 mysqld_exporter)配了
WITH GRANT OPTION,结果它连上后意外执行了GRANT PROCESS ON *.* TO 'hacker'@'%' -
GRANT ALL ON *.*在 MySQL 8.0 中实际包含APPLICATION_PASSWORD_ADMIN等新权限,可能绕过双因子策略 - 用
mysqldump --all-databases备份时,如果连接用户有LOCK TABLES但没SELECT,备份会失败——最小权限不是“越少越好”,而是“刚好够用”
复杂点在于:权限检查在 server 层,而存储引擎(如 InnoDB)还有自己的行级限制,两者不联动。想做到真正最小权限,得在应用层做字段过滤,数据库层只管库/表/语句级。










