普通用户默认不能查ALL_TAB_COLUMNS,但获授SELECT ANY DICTIONARY或SELECT_CATALOG_ROLE后即可绕过对象权限查看所有schema表结构;应立即回收该权限,改用最小化授权或视图+VPD过滤。

普通用户默认就能查 ALL_TAB_COLUMNS?问题出在这儿
Oracle 普通用户只要被授予 SELECT ANY DICTIONARY 或 SELECT_CATALOG_ROLE,就能直接查 ALL_TAB_COLUMNS、ALL_OBJECTS 甚至 DBA_TAB_COLUMNS —— 这意味着他能绕过对象权限,看到所有用户(包括其他 schema)的表结构定义。这不是 bug,是 Oracle 字典视图的默认行为:只要用户有“读字典”类权限,ALL_* 视图就会展开全部可访问对象,不校验你是否对那个表本身有 SELECT 权限。
禁用 SELECT ANY DICTIONARY 是最有效的一刀
绝大多数业务场景根本不需要普通用户查别人家的表结构。真正需要的只是查自己创建的表,或被明确授权访问的表。所以第一步必须回收高危系统权限:
- 检查谁有:
SELECT * FROM dba_role_privs WHERE granted_role IN ('SELECT_ANY_DICTIONARY', 'SELECT_CATALOG_ROLE') AND grantee NOT IN ('SYS', 'SYSTEM', 'DBA'); - 回收权限:
REVOKE SELECT ANY DICTIONARY FROM 用户名;(注意:不能用REVOKE SELECT_CATALOG_ROLE FROM ...直接回收角色,得先REVOKE SELECT_CATALOG_ROLE FROM 用户名;再确认该角色没被嵌套授予) - 如果用户确实需要查数据字典(比如 DBA 工具账号),改用最小化授权:只给
SELECT权限在特定字典视图上,例如GRANT SELECT ON SYS.ALL_TAB_COLUMNS TO 用户名;,但这样仍可能暴露其他用户结构——更稳妥的是用视图包装 + 行级安全(见下一条)
用视图封装 + VPD 实现按 schema 过滤
如果你无法完全回收字典权限(比如某些中间件依赖 SELECT_CATALOG_ROLE),又必须限制结构可见性,就得主动干预查询结果。核心思路是:不让用户直查 ALL_TAB_COLUMNS,而是通过自定义视图 + 虚拟专用数据库(VPD)策略,自动注入 OWNER = USER 条件:
- 创建同义词屏蔽原视图:
CREATE SYNONYM my_tab_columns FOR sys.all_tab_columns; - 建一个包装视图:
CREATE VIEW my_tab_columns AS SELECT * FROM sys.all_tab_columns WHERE owner = USER; - 然后只授这个视图的
SELECT权限:GRANT SELECT ON my_tab_columns TO 用户名; - 进阶做法:在
sys.all_tab_columns上启用 VPD 策略函数,让每次查询都自动追加owner = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA')—— 但这要求用户有EXEMPT ACCESS POLICY以外的权限,实操中容易引发连锁授权问题,慎用
别忘了 DBMS_METADATA.GET_DDL 的后门风险
即使封死了字典视图,用户仍可能用 DBMS_METADATA.GET_DDL('TABLE', 'T_NAME', 'OTHER_USER') 获取他人表的 DDL。这个包默认不限制 schema,只要用户有 SELECT_CATALOG_ROLE 就能调用。解决方法只有两个:
- 彻底回收
SELECT_CATALOG_ROLE(推荐) - 或者用细粒度审计 + 触发器拦截非法调用(不推荐,性能差且难维护)
真正容易被忽略的是:很多团队只盯着 SELECT ANY TABLE,却放过了 SELECT_CATALOG_ROLE 这个“字典穿透器”。它不显眼,但危害比多数 DML 权限还大——因为结构信息一旦泄露,攻击面就从数据层直接升维到设计层。











