如何设置MySQL临时只读权限,为开发人员创建短期有效的只读账号?

2026-04-30 21:161阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计802个文字,预计阅读时间需要4分钟。

如何设置MySQL临时只读权限,为开发人员创建短期有效的只读账号?

MySQL不支持CREATE USER ... IDENTIFIED BY 'pwd' EXPIRE AFTER 7 DAY这类语法(那是MariaDB的特性),也没有内置的READ_ONLY_ROLE可直接分配给用户。所谓临时只读权限,必须人工控制生命周期:

创建带 IP 限制和库级 SELECT 的只读账号

先建用户,限定来源更安全:

CREATE USER 'dev_ro_2026q2'@'10.20.30.%' IDENTIFIED BY 'ro_temp_8xK!';

再授最小权限 —— 注意三点:

  • 必须用反引号包裹数据库名,比如 `billing-prod`,否则含短横的库名会报语法错误
  • 不能写 GRANT SELECT ON *.*,否则会暴露 mysqlinformation_schema 等系统库
  • 如果开发只需查一张表(如日志归档表),就精确到表:GRANT SELECT ON `logdb`.`events_202604` TO 'dev_ro_2026q2'@'10.20.30.%';

执行完记得刷新(MySQL 5.7 或某些容器部署仍依赖它):

FLUSH PRIVILEGES;

必须显式回收高危权限,不能只靠“没授就不算有”

新建用户默认只有 USAGE 权限,看似安全,但以下情况会导致意外越权:

  • 该用户名在其他 host 上已有残留记录(比如 'dev_ro_2026q2'@'%'),权限会叠加
  • 旧账号被复制同步到从库,而从库开启了 read_only=0,等于开了后门
  • 没收回 FILE 权限,用户可用 SELECT ... INTO OUTFILE 写服务器文件
  • 漏掉 EXECUTE,若库中有存储过程,可能间接触发写操作

稳妥做法是主动撤销所有非 SELECT 权限:

REVOKE INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, INDEX, LOCK TABLES, EXECUTE, FILE ON *.* FROM 'dev_ro_2026q2'@'10.20.30.%';

再检查一遍:SHOW GRANTS FOR 'dev_ro_2026q2'@'10.20.30.%'; —— 输出里应该只有 SELECT 行。

验证时别用旧连接,要开新会话测 INSERT 是否真被拒

权限变更对已存在的连接不生效。常见误判是:

  • 用 root 改完权限后,立刻用同一个终端切到该用户测试,结果发现 INSERT 居然成功 —— 其实是旧会话缓存了权限
  • 忘了删掉同名但不同 host 的冗余账号,比如留着 @'%' 记录,导致实际匹配到了更高权限项

正确验证步骤:

  • 新开一个终端,用 mysql -u dev_ro_2026q2 -p -h your-db-host 连接
  • 执行 SELECT DATABASE(); 确认当前库是否为你授权的目标库
  • 执行 INSERT INTO your_target_db.users VALUES (); —— 必须报错 ERROR 1142 (42000): INSERT command denied
  • 执行 SELECT * FROM information_schema.TABLES LIMIT 1; —— 应允许(元数据可读),但 SELECT * FROM other_db.users; 必须拒绝

真正麻烦的不是建账号,而是后续没人记得这个账号该在哪天删。建议把清理动作写进运维排期,或者用脚本定期扫描 user 表里含 _ro__2026q2 这类命名模式的账号并告警。

标签:Mysql

本文共计802个文字,预计阅读时间需要4分钟。

如何设置MySQL临时只读权限,为开发人员创建短期有效的只读账号?

MySQL不支持CREATE USER ... IDENTIFIED BY 'pwd' EXPIRE AFTER 7 DAY这类语法(那是MariaDB的特性),也没有内置的READ_ONLY_ROLE可直接分配给用户。所谓临时只读权限,必须人工控制生命周期:

创建带 IP 限制和库级 SELECT 的只读账号

先建用户,限定来源更安全:

CREATE USER 'dev_ro_2026q2'@'10.20.30.%' IDENTIFIED BY 'ro_temp_8xK!';

再授最小权限 —— 注意三点:

  • 必须用反引号包裹数据库名,比如 `billing-prod`,否则含短横的库名会报语法错误
  • 不能写 GRANT SELECT ON *.*,否则会暴露 mysqlinformation_schema 等系统库
  • 如果开发只需查一张表(如日志归档表),就精确到表:GRANT SELECT ON `logdb`.`events_202604` TO 'dev_ro_2026q2'@'10.20.30.%';

执行完记得刷新(MySQL 5.7 或某些容器部署仍依赖它):

FLUSH PRIVILEGES;

必须显式回收高危权限,不能只靠“没授就不算有”

新建用户默认只有 USAGE 权限,看似安全,但以下情况会导致意外越权:

  • 该用户名在其他 host 上已有残留记录(比如 'dev_ro_2026q2'@'%'),权限会叠加
  • 旧账号被复制同步到从库,而从库开启了 read_only=0,等于开了后门
  • 没收回 FILE 权限,用户可用 SELECT ... INTO OUTFILE 写服务器文件
  • 漏掉 EXECUTE,若库中有存储过程,可能间接触发写操作

稳妥做法是主动撤销所有非 SELECT 权限:

REVOKE INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, INDEX, LOCK TABLES, EXECUTE, FILE ON *.* FROM 'dev_ro_2026q2'@'10.20.30.%';

再检查一遍:SHOW GRANTS FOR 'dev_ro_2026q2'@'10.20.30.%'; —— 输出里应该只有 SELECT 行。

验证时别用旧连接,要开新会话测 INSERT 是否真被拒

权限变更对已存在的连接不生效。常见误判是:

  • 用 root 改完权限后,立刻用同一个终端切到该用户测试,结果发现 INSERT 居然成功 —— 其实是旧会话缓存了权限
  • 忘了删掉同名但不同 host 的冗余账号,比如留着 @'%' 记录,导致实际匹配到了更高权限项

正确验证步骤:

  • 新开一个终端,用 mysql -u dev_ro_2026q2 -p -h your-db-host 连接
  • 执行 SELECT DATABASE(); 确认当前库是否为你授权的目标库
  • 执行 INSERT INTO your_target_db.users VALUES (); —— 必须报错 ERROR 1142 (42000): INSERT command denied
  • 执行 SELECT * FROM information_schema.TABLES LIMIT 1; —— 应允许(元数据可读),但 SELECT * FROM other_db.users; 必须拒绝

真正麻烦的不是建账号,而是后续没人记得这个账号该在哪天删。建议把清理动作写进运维排期,或者用脚本定期扫描 user 表里含 _ro__2026q2 这类命名模式的账号并告警。

标签:Mysql