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_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,确认目标权限不再出现在有效权限集合中。
综上,角色与权限分析的关键动作是:识别预定义角色与自定义角色的边界,展开角色继承链,计算有效权限,排查读写冲突、职责分离冲突和转授权风险,并用数据字典视图形成可复查、可整改的审计闭环。