REVOKE 权限需同时撤销表级 SELECT 及 schema 级 USAGE 才能彻底隐藏表名,否则 dt 仍可见;VIEW 须用 SQL SECURITY DEFINER 且避免敏感字段;SQL Server 须用 sys.fn_my_permissions 验证权限是否真正剥离。

PostgreSQL 中用 REVOKE 剥离用户对特定表的 SELECT 权限
直接撤掉权限是最常见也最有效的隐藏方式,但很多人只撤了 SELECT,忘了 USAGE 和 SELECT 在 schema 层的组合效应。用户仍可能通过 dt 或 pg_tables 看到表名——因为表名本身不依赖行级权限,而取决于 schema 的 USAGE。
- 先确认目标用户当前拥有的权限:
SELECT grantee, privilege_type FROM information_schema.role_table_grants WHERE table_name = 'sensitive_table'; - 撤销核心权限(缺一不可):
REVOKE SELECT ON TABLE sensitive_table FROM app_user;+REVOKE INSERT, UPDATE, DELETE ON TABLE sensitive_table FROM app_user; - 如果用户不需要访问整个 schema,顺手收回 schema 级
USAGE:REVOKE USAGE ON SCHEMA public FROM app_user;(否则dt仍会列出所有表) - 注意:
REVOKE不会级联,父 role 的权限不会自动失效;若用户是通过 group role 获得权限,需在 group 上操作或用REASSIGN OWNED配合DROP OWNED
MySQL 8.0+ 用 CREATE VIEW 替代直接暴露原表
物理隐藏不如逻辑隔离可靠——尤其当 DBA 无法控制应用连接账号时,VIEW 是更可控的出口。但别把 VIEW 当权限开关:用户只要对 VIEW 有 SELECT,就默认能查,和底层表权限无关;真正起作用的是建 VIEW 时用的 SQL SECURITY DEFINER。
- 创建带安全上下文的 VIEW:
CREATE VIEW safe_user_list AS SELECT id, name, created_at FROM users WHERE status = 'active' SQL SECURITY DEFINER; -
SQL SECURITY DEFINER表示执行时以定义者(通常是 DBA)身份校验权限,用户无需对原表有权限 - 务必避免在 VIEW 定义中引用敏感字段(如
password_hash),且不要用UNION拼接不同权限级别的表——MySQL 会校验所有分支的权限 - 测试时用目标用户登录后执行:
SELECT * FROM safe_user_list;,再尝试SELECT * FROM users;确认报错ERROR 1142 (42000): SELECT command denied to user
SQL Server 中 sys.fn_my_permissions 检查权限是否真被剥离
光跑 REVOKE 不等于权限消失,SQL Server 的权限继承链复杂(database → schema → object → column),常出现“以为撤了,其实还能查”的情况。必须用系统函数验证最终效果。
- 切换到目标用户上下文后运行:
SELECT * FROM sys.fn_my_permissions('sensitive_table', 'OBJECT'); - 结果为空 = 真没权限;若仍有
SELECT,说明权限来自更高层(比如 schema 的db_datareader角色),得去角色里查 - 检查角色继承:
SELECT r.name AS role_name, m.name AS member_name FROM sys.database_role_members rm JOIN sys.database_principals r ON rm.role_principal_id = r.principal_id JOIN sys.database_principals m ON rm.member_principal_id = m.principal_id WHERE m.name = 'app_user'; - 别忽略列级权限:即使表级
SELECT被撤,GRANT SELECT ON sensitive_table(password_hash) TO app_user;仍会让某列可读
Schema 隔离不是权限替代方案,而是补充手段
把敏感表挪到 private schema 并收回该 schema 的 USAGE,确实能让 information_schema.tables 查不到——但这只是藏名字,不是藏数据。一旦用户知道表存在,且 schema 名泄露(比如日志、错误消息、开发环境残留),权限缺失就形同虚设。
- schema 隔离适合配合权限使用:先
CREATE SCHEMA private;,再ALTER TABLE users SET SCHEMA private;,最后REVOKE USAGE ON SCHEMA private FROM PUBLIC; - PostgreSQL 中,
pg_tables默认只查current_schemas(false)包含的 schema;但SELECT * FROM pg_class WHERE relname = 'users'仍可见,因为这是 catalog 级查询 - MySQL 和 SQL Server 没有真正的 schema 隔离概念(MySQL 的 database ≈ schema,SQL Server 的 schema 更像命名空间),强行类比容易误判
- 最易被忽略的一点:应用 ORM(如 Django、Hibernate)常自动生成
INFORMATION_SCHEMA查询来推断结构,这类查询走的是连接用户的权限,不是应用代码的逻辑权限