如何比较两个Oracle用户的权限差异

作者:袖梨 2026-08-31

直接对比两个用户系统权限差异应查询DBA_SYS_PRIVS视图,用MINUS操作获取精确差集,注意ADMIN_OPTION不同(YES/NO)属实质差异;对象权限需联合DBA_TAB_PRIVS和DBA_COL_PRIVS比对,且必须包含OWNER和COLUMN_NAME;角色权限须递归展开验证,DBA角色不保证等价,需逐项检查关键系统权限及SELECT ANY DICTIONARY等高危权限。

直接对比两个用户系统权限的差异

DBA_SYS_PRIVS 视图能最准地看出谁多了哪些系统级权限(比如 CREATE ANY TABLEALTER SYSTEM)。别依赖角色间接推断,因为角色可能被授予不同子集,必须查实际生效的权限。

执行以下查询(需有访问 DBA_SYS_PRIVS 的权限,如 SYS 或已授 SELECT_CATALOG_ROLE):

SELECT privilege, admin_optionFROM DBA_SYS_PRIVSWHERE grantee = 'USER_A'MINUSSELECT privilege, admin_optionFROM DBA_SYS_PRIVSWHERE grantee = 'USER_B';

再反向查一遍,就能得到完整差异。注意:ADMIN_OPTION 值不同(YES vs NO)也算实质差异,意味着能否转授该权限。

  1. 如果某权限在 USER_A 中存在但 USER_B 中没有,说明 USER_A 多了这项能力
  2. MINUS 不区分大小写,但用户名必须全大写(Oracle 默认存储为大写)
  3. 结果为空不代表权限完全一致——可能只是系统权限相同,对象权限或角色还没比

查表级权限(对象权限)差异时别漏掉 schema 和列级细节

DBA_TAB_PRIVS 是核心视图,但它字段多、结构松散,容易漏判。关键字段包括 GRANTEE(被授权人)、OWNER(对象所有者)、TABLE_NAMEPRIVILEGE(如 SELECTINSERT)、COLUMN(是否为列级权限)和 GRANTABLE(是否带 WITH GRANT OPTION)。

列级权限(如 GRANT SELECT(id) ON scott.emp TO user_a)会出现在 DBA_COL_PRIVS 里,DBA_TAB_PRIVS 不包含它。所以必须联合查:

SELECT owner, table_name, privilege, column_nameFROM (SELECT owner, table_name, privilege, NULL AS column_nameFROM DBA_TAB_PRIVSWHERE grantee = 'USER_A'MINUSSELECT owner, table_name, privilege, NULL AS column_nameFROM DBA_TAB_PRIVSWHERE grantee = 'USER_B')UNION ALLSELECT owner, table_name, privilege, column_nameFROM DBA_COL_PRIVSWHERE grantee = 'USER_A'AND (owner, table_name, privilege, column_name) NOT IN (SELECT owner, table_name, privilege, column_nameFROM DBA_COL_PRIVSWHERE grantee = 'USER_B');
  1. 只比 TABLE_NAME 不行,必须带上 OWNER,否则跨 schema 同名表会被混淆
  2. GRANTABLE = 'YES' 表示可转授,这是安全敏感点,差异时要单独标出
  3. 如果用户有 SELECT ANY TABLE 这类系统权限,DBA_TAB_PRIVS 里不会体现——它只记录显式授予的对象权限

角色继承链导致的隐性权限差异最难排查

两个用户都拥有 CONNECTRESOURCE 角色,但实际权限可能天差地别——因为角色本身可能被进一步授予其他角色或权限,且 WITH ADMIN OPTION 允许层层传递。

查角色直接授予情况用:

SELECT granted_role, admin_option, default_roleFROM DBA_ROLE_PRIVSWHERE grantee IN ('USER_A', 'USER_B');

但真正麻烦的是递归展开:比如 USER_A 被授了 ROLE_X,而 ROLE_X 又被授了 ROLE_Y,后者含 DROP ANY TABLEUSER_B 没有 ROLE_X,但直接被授了 ROLE_Z,而 ROLE_Z 也含 DROP ANY TABLE —— 表面看权限一样,实则来源路径不同,回收风险完全不同。

  1. Oracle 不提供开箱即用的“角色展开树”视图,得靠脚本递归查 ROLE_ROLE_PRIVS
  2. DBA_ROLE_PRIVS.ADMIN_OPTION = 'YES' 表示该用户能把这个角色再授给别人,这是权限扩散的关键入口
  3. SELECT * FROM ROLE_SYS_PRIVS WHERE role = 'xxx' 查角色内含哪些系统权限,但注意:角色里的权限可能被 REVOKE 单独收回,不等于角色定义

DBA 权限这种超级角色要单独验,不能只看角色名

DBA 角色不是原子权限,它由约 150+ 个系统权限和大量对象权限组成。即使两个用户都被授予 DBA,也不能认为权限等价——因为 Oracle 允许对 DBA 中的单个权限做 REVOKE(例如 REVOKE ALTER SYSTEM FROM user_a),这在审计中常被忽略。

验证是否真有完整 DBA 权限,不能只查 DBA_ROLE_PRIVS

SELECT privilegeFROM DBA_SYS_PRIVSWHERE grantee = 'USER_A'AND privilege NOT IN (SELECT privilege FROM ROLE_SYS_PRIVS WHERE role = 'DBA');

再反过来查缺失项。更实用的做法是检查关键高危权限是否存在:

  1. ALTER SYSTEMCREATE ANY PROCEDUREUNDER ANY TABLE 这些是 DBA 的标志性权限,逐个确认
  2. SELECT ANY DICTIONARY 允许读取数据字典,常被用于绕过应用层审计,必须单列检查
  3. 在 CDB/PDB 环境下,DBA 在根容器(CDB$ROOT)和 PDB 中含义不同,跨容器查询必须指定 CONTAINER = 'ALL' 或切到对应 PDB

权限差异最终要落到“谁能做什么”上,而不是“谁被授了什么”。尤其当涉及生产账号比对时,WITH ADMIN OPTION 和列级权限这类细节,往往才是越权操作的实际入口。

相关文章

精彩推荐