SQL GRANT 与 REVOKE 权限策略优化

1次阅读

应严格遵循最小权限原则,显式授予具体库表的必要权限,避免使用all privileges和.;注意mysql版本差异、grant option残留、跨库join需分别授权、host匹配精度及连接池缓存影响。

SQL GRANT 与 REVOKE 权限策略优化

GRANT 时别直接给 ALL PRIVILEGES,哪怕只是临时测试

很多 dba 或开发在建测试账号时随手写 GRANT ALL PRIVILEGES ON *.* TO 'test'@'%',结果发现权限收不回来——REVOKEALL PRIVILEGES 的撤销行为在 MySQL 8.0+ 和旧版本逻辑不同,且可能漏掉 GRANT OPTION 或列级权限。更麻烦的是,一旦该账号被误授了 PROXYCREATE USER 等高危权限,后续 REVOKE 不会自动清理这些隐式能力。

实操建议:

  • 始终按最小权限原则,显式列出需要的权限,比如 selectINSERTUPDATE(仅限具体库表)
  • 避免用 *.*,优先写成 `myapp_db`.*`myapp_db`.`orders`
  • 如果真要批量授权,用 SHOW GRANTS for 'user'@'host' 先确认当前权限快照,再比对变更
  • MySQL 8.0+ 中,ALL PRIVILEGES 不再包含 APPLICATION_PASSWORD_ADMIN 等新权限,但旧版本升级后容易误判

REVOKE 失效?先查清权限层级和 WITH GRANT OPTION 是否残留

REVOKE SELECT ON `sales`.* FROM 'analyst'@'10.0.2.%' 执行成功,但用户仍能查数据——大概率是权限来自更高层级(比如 GRANT SELECT ON *.*),或该用户曾被授予 WITH GRANT OPTION 并转授给了别人,而你只 revoke 了原始账号。

实操建议:

  • SELECT * FROM mysql.role_edges(MySQL 8.0+)或 SELECT * FROM mysql.user WHERE User='xxx' 检查是否启用了角色继承
  • 运行 SHOW GRANTS FOR 'analyst'@'10.0.2.%',注意输出里是否含 GRANT OPTION;若有,必须先 REVOKE GRANT OPTION ON ... 再 revoke 具体权限
  • 权限生效有缓存:MySQL 5.7+ 默认启用 flush privileges 自动刷新,但若手动改过 mysql.user 表,必须显式执行 FLUSH PRIVILEGES
  • PostgreSQL 用户注意:REVOKE 不支持 CASCADE 默认行为,需加 restrictCASCADE 显式声明

跨库查询权限不够?不是漏授 SELECT,而是没开 SELECT INTO OUTFILE 或函数权限

用户能连上、能查本库表,但一执行 SELECT * FROM db1.t1 JOIN db2.t2 就报 Error 1142 (42000): SELECT command denied to user——这不是权限没给全,而是 MySQL 默认把跨库 JOIN 视为对“所有涉及库”的独立访问请求,每个库都得单独授权。

实操建议:

  • 必须分别执行:GRANT SELECT ON `db1`.* TO 'user'@'host'GRANT SELECT ON `db2`.* TO 'user'@'host'
  • 如果语句里调用了自定义函数或存储过程,还需额外 GRANT EXECUTE ON function `db1`.`my_func`
  • 使用视图时,权限检查发生在视图创建者(DEFINER)或调用者(SQL SECURITY)层面,容易误以为授了表权限就万事大吉
  • MySQL 8.0.16+ 支持 READ_ONLY_ROUTINE 权限控制函数调用,但默认不启用,别指望它自动兜底

权限变更后应用连不上?重点盯 max_connectionshost 匹配精度

明明 GRANT 成功、SHOW GRANTS 也对得上,但应用启动时报 access denied for user 'app'@'192.168.1.100'——八成是 host 字段匹配失败。MySQL 权限系统严格按 User+Host 组合查表,'app'@'localhost''app'@'127.0.0.1' 完全是两个账号。

实操建议:

  • 确认连接来源 IP,用 SELECT USER(), CURRENT_USER() 在会话里看实际匹配到哪个账号
  • 避免用 '%' 通配符替代具体网段,尤其在多网卡或容器环境里,'app'@'10.0.2.%''app'@'%' 更安全且不易冲突
  • 权限变更后,某些客户端(如 PHP pdo)会复用连接池里的旧连接,导致新权限不生效,可临时设 wait_timeout=10 快速验证
  • 注意 max_connections全局变量,但每个账号还能单独设 MAX_CONNECTIONS_PER_HOUR,这个限制在 GRANT 语句里容易被忽略

权限这事,细节全在 Host 字符串、权限层级嵌套、以及不同 MySQL 版本对 GRANT OPTION 的处理差异里。越想“一劳永逸”地批量授权,越容易掉进撤销不了、查不到、连不上的三重坑。

text=ZqhQzanResources