4. 角色与权限分析

在 Oracle 权限审计中,角色是权限的“打包容器”。直接审计用户被授予的系统权限和对象权限,往往只能看到冰山一角;真正决定一个用户能做什么的,是它通过角色间接获得的权限集合。因此,角色与权限分析的核心任务是:识别用户实际持有的角色、展开角色继承链、计算有效权限,并发现权限冲突与过度授权。

一、预定义角色与自定义角色的区别

Oracle 预定义角色由数据库安装脚本创建,常见的有 DBA、CONNECT、RESOURCE、SELECT_CATALOG_ROLE、EXECUTE_CATALOG_ROLE、DELETE_CATALOG_ROLE、EXP_FULL_DATABASE、IMP_FULL_DATABASE、AQ_ADMINISTRATOR_ROLE、SCHEDULER_ADMIN 等。它们的特点是权限范围大、面向通用管理场景,例如 DBA 几乎拥有全部系统权限,SELECT_CATALOG_ROLE 可读取大量数据字典视图。

自定义角色由 DBA 或安全管理员按业务需要创建,例如 APP_READONLY、APP_WRITER、AUDIT_VIEWER。自定义角色的价值在于最小权限:只授予完成特定工作所需的系统权限和对象权限,避免把 DBA 直接授予应用账号。审计时应重点关注:哪些用户持有预定义高权限角色、哪些自定义角色被授予了超出命名含义的权限。

二、角色继承与有效权限展开

角色可以授予角色,形成继承链。例如 APP_WRITER 被授予 APP_READONLY,则持有 APP_WRITER 的用户同时拥有 APP_READONLY 的全部权限。Oracle 默认角色在登录时自动启用,但也可以通过 ALTER USER ... DEFAULT ROLE 限制默认启用范围,或使用 SET ROLE 在会话中动态启用。审计时必须区分“被授予的角色”和“会话中实际启用的角色”。

展开有效权限的基本路径是:从 DBA_ROLE_PRIVS 找到用户直接持有的角色,再通过 ROLE_ROLE_PRIVS 递归展开角色之间的授予关系,最后用 ROLE_SYS_PRIVS 和 ROLE_TAB_PRIVS 取出每个角色包含的系统权限与对象权限。下面这条查询用于查看某个用户直接持有的角色及其默认启用状态:

SELECT grantee, granted_role, admin_option, default_role
FROM   dba_role_privs
WHERE  grantee = 'APP_USER'
ORDER  BY granted_role;

-- 展开角色继承链
SELECT role, granted_role, admin_option
FROM   role_role_privs
START WITH role = 'APP_WRITER'
CONNECT BY PRIOR granted_role = role;

-- 查看角色包含的系统权限
SELECT role, privilege, admin_option
FROM   role_sys_privs
WHERE  role IN ('APP_WRITER','APP_READONLY');

-- 查看角色包含的对象权限
SELECT role, owner, table_name, privilege, grantable
FROM   role_tab_privs
WHERE  role IN ('APP_WRITER','APP_READONLY');

把上述结果合并,就得到用户的“有效权限视图”。如果用户还通过 DBA_SYS_PRIVS、DBA_TAB_PRIVS 直接持有权限,也要一并纳入。审计结论应回答三个问题:用户最终能访问哪些对象、能执行哪些系统操作、这些权限是否与其岗位职责匹配。

角色继承与有效权限展开示意 用户 APP_USER 默认角色:APP_WRITER APP_WRITER 自定义角色(可写) APP_READONLY 自定义角色(只读) DBA 预定义高权限角色 系统权限 CREATE SESSION、CREATE TABLE SELECT ANY DICTIONARY(来自 DBA) 对象权限 HR.EMPLOYEES:SELECT、INSERT HR.DEPARTMENTS:SELECT 冲突/风险点 APP_USER 同时持有 DBA 存在过度授权与职责分离冲突 继承 审计要点 1. 用 DBA_ROLE_PRIVS 查直接角色,用 ROLE_ROLE_PRIVS 递归展开继承链,避免遗漏间接权限。 2. 区分 DEFAULT_ROLE=YES/NO:未默认启用的角色在普通会话中不生效,但可被 SET ROLE 激活。 3. 检查 ADMIN_OPTION=YES 的角色授予,持有者可将该角色转授他人,属于高风险路径。 4. 对同时持有 APP_WRITER 与 DBA 的账号,应判定为职责分离冲突,建议回收 DBA 或改用最小权限角色。

三、权限冲突与职责分离排查

权限冲突通常表现为三类。第一类是“读写冲突”:同一用户既持有只读角色又持有写入角色,若业务上要求该用户仅查询,则写入权限属于过度授权。第二类是“职责分离冲突”:例如制单与审核、开发与生产运维本应由不同账号承担,但同一用户同时持有 APP_WRITER 和 DBA,就破坏了职责分离。第三类是“转授权风险”:DBA_ROLE_PRIVS.ADMIN_OPTION='YES' 表示该用户可以把这个角色再授予别人,形成权限扩散路径。

排查时可以先找出所有持有高权限预定义角色的用户:

-- 持有 DBA 角色的用户
SELECT grantee, admin_option, default_role
FROM   dba_role_privs
WHERE  granted_role = 'DBA';

-- 同时持有写入角色与 DBA 的用户(职责分离冲突)
SELECT a.grantee
FROM   dba_role_privs a
JOIN   dba_role_privs b ON a.grantee = b.grantee
WHERE  a.granted_role = 'APP_WRITER'
AND    b.granted_role = 'DBA';

-- 可转授的角色授予
SELECT grantee, granted_role
FROM   dba_role_privs
WHERE  admin_option = 'YES'
AND    granted_role NOT IN ('DBA');

对于对象权限冲突,可以借助 DBA_TAB_PRIVS 与 ROLE_TAB_PRIVS 对比:如果某用户通过角色获得了 HR.EMPLOYEES 的 SELECT,又直接持有该表的 DELETE,而岗位只要求查询,则直接授予的 DELETE 应被回收。审计输出建议形成一张“用户—角色—权限—风险等级”的清单,风险等级可按“是否含 DBA、是否可转授、是否跨职责”三个维度判定。

角色继承链可能很深,手工展开容易遗漏。可以先用 CONNECT BY 把 ROLE_ROLE_PRIVS 展开成完整路径,再与 ROLE_SYS_PRIVS、ROLE_TAB_PRIVS 做连接,得到每个用户的有效权限全集。对权限数量较多的库,建议把结果物化到审计中间表,便于定期比对差异。

四、用数据字典视图形成审计闭环

角色与权限审计常用的数据字典视图可以归纳为:DBA_ROLES(角色清单及是否预定义)、DBA_ROLE_PRIVS(用户/角色被授予的角色)、ROLE_ROLE_PRIVS(角色之间的继承)、ROLE_SYS_PRIVS(角色含有的系统权限)、ROLE_TAB_PRIVS(角色含有的对象权限)、DBA_SYS_PRIVS(用户直接持有的系统权限)、DBA_TAB_PRIVS(用户直接持有的对象权限)、DBA_USERS(账号状态与默认表空间)。

一个可复用的审计查询思路是:先锁定审计对象(如 APP_USER),再分别取“直接系统权限 + 直接对象权限 + 角色展开后的系统权限 + 角色展开后的对象权限”,最后按权限类型分组统计。若发现某用户的有效权限集合明显大于其岗位所需,就进入人工复核,确认是历史遗留、临时授权未回收,还是配置错误。审计完成后,应把确认的过度授权记录为整改项,明确回收对象、回收方式和验证方法,例如回收后重新查询 DBA_ROLE_PRIVS 与 ROLE_SYS_PRIVS,确认目标权限不再出现在有效权限集合中。

不要只检查用户直接持有的权限。很多“看起来干净”的账号,其真正的高权限来自角色继承链中的某一层。审计结论必须以“有效权限”为准,而不是以直接授予为准。

综上,角色与权限分析的关键动作是:识别预定义角色与自定义角色的边界,展开角色继承链,计算有效权限,排查读写冲突、职责分离冲突和转授权风险,并用数据字典视图形成可复查、可整改的审计闭环。