mysql 8.0+ 推荐用 create role 统一管理只读权限,避免漏库、权限漂移;postgresql 需结合 schema 权限与 default privileges,并严格过滤系统库和敏感函数。
mysql 8.0+ 中用 create role 统一管理只读权限
直接建角色比逐库 grant select 更可靠,避免漏库、权限漂移或误加写权限。mysql 8.0 开始支持角色(role),这是唯一能真正“全局”控制只读访问的机制。
常见错误是给用户直接 GRANT SELECT ON *.* —— 这会暴露 information_schema、performance_schema 等系统库,且无法限制未来新建库的访问。角色天然隔离作用域,后续只需 GRANT role_name TO user 即可生效。
CREATE ROLE 'readonly_role';-
GRANT SELECT ON `myapp\_%`.* TO 'readonly_role';(用前缀匹配业务库,如myapp_prod、myapp_staging) - 不显式授权
mysql、sys等系统库,角色默认无权访问 - 执行
SET DEFAULT ROLE 'readonly_role' TO 'app_user'@'%';让用户自动继承权限
PostgreSQL 里用 DEFAULT PRIVILEGES 捕获新表,但库级仍需手动处理
PostgreSQL 没有“库级角色”,GRANT SELECT ON DATABASE 实际无效——它只允许连接,不赋予任何表访问权。真正起效的是对 schema 的权限,以及对现有/未来表的默认权限设置。
容易踩的坑:只运行 GRANT SELECT ON ALL TABLES IN SCHEMA public,却忘了新创建的表默认无权限,应用一写新表就报 permission denied for table xxx。
- 先
GRANT CONNECT ON DATABASE mydb TO readonly_group; - 再
GRANT USAGE ON SCHEMA public TO readonly_group; - 对已有表:
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_group; - 对后续新建表:
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_group; - 如果业务用多个 schema(如
analytics、logs),每 schema 都要重复上述三步
权限同步脚本必须排除系统库和临时库
无论用 MySQL 还是 PostgreSQL,写自动化脚本批量授只读权时,最常出问题的是没过滤掉不该碰的库。比如 MySQL 的 sys 库含敏感视图,PostgreSQL 的 pg_catalog 被误授 SELECT 后,攻击者能枚举所有用户密码哈希(pg_authid)。
另一个隐形雷区是测试库命名不规范,比如叫 test、tmp_xxx 或带数字后缀(app_v2),正则或 LIKE 匹配时一并扫进去了。
- MySQL 排除列表:
WHERE schema_name NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys') - PostgreSQL 排除条件:
AND datname NOT IN ('postgres', 'template0', 'template1'),且跳过pg_开头的 schema - 建议用白名单:只对明确以
prod_、core_等前缀开头的库操作,比黑名单更安全
只读 ≠ 安全,SELECT 可能触发函数或泄露元数据
很多团队以为加了 SELECT 就万事大吉,但实际中 SELECT 权限可能被绕过或放大。例如 MySQL 的 LOAD_FILE() 函数不需要额外权限,只要用户能查到含路径的字段,就能读取服务器任意文件;PostgreSQL 的 pg_read_file() 同理,且 SELECT 权限配合 pg_stat_activity 可看到其他用户正在执行的 SQL。
真正可控的只读,必须叠加函数禁用和视图封装。
- MySQL:启动时加
--secure-file-priv=""并禁用LOCAL INFILE,或在账号级REVOKE FILE ON *.* FROM 'user' - PostgreSQL:用
REVOKE EXECUTE ON FUNCTION pg_read_file(text, bigint, bigint) FROM readonly_group; - 敏感字段(如密码重置 token、密钥)不要裸表暴露,应通过只读视图过滤列
权限不是设完就稳了,尤其当数据库版本升级、新函数加入或业务加了 UDF,得定期 audit SHOW GRANTS 和 \z 输出。










