直接查role_sys_privs可定位含select any table等any权限的角色,重点排查dba、exp_full_database等高危角色,需验证权限链、警惕public被授any权限。

查哪些角色含 SELECT ANY TABLE 等 ANY 权限
直接查 ROLE_SYS_PRIVS 就行,它专为“角色 → 系统权限”映射而设。重点不是看用户,而是先锁定哪些角色本身被硬编码了高危权限。
常见错误是跳过这步、直接查用户权限,结果漏掉 DBA、EXP_FULL_DATABASE、RESOURCE 这类自带 ANY 权限的角色——它们才是风险扩散的源头。
SELECT role, privilege FROM ROLE_SYS_PRIVS WHERE privilege LIKE '%ANY%';- 加
ORDER BY role更容易发现嵌套关系(比如APP_ADMIN没列 ANY,但它的上级角色DBA有) - 注意大小写:
privilege字段值全大写,'select any table'查不到,必须用'SELECT ANY TABLE'
确认 DBA 角色是否真的包含 SELECT ANY TABLE
DBA 是个聚合角色,不保证所有版本都显式包含每条 ANY 权限,尤其在最小化安装或自定义加固过的库中。不能凭经验跳过验证。
执行:SELECT privilege FROM ROLE_SYS_PRIVS WHERE role = 'DBA' AND privilege = 'SELECT ANY TABLE';
如果没返回,说明当前环境里 DBA 并未直接赋予该权限——但别松懈,得继续查它所依赖的其他角色(比如 SELECT_CATALOG_ROLE 或自定义中间角色),因为权限链可能更深。
-
DBA角色本身可能通过WITH ADMIN OPTION授予他人,导致权限二次扩散 - 某些 Oracle 版本中,
DBA不显式含SELECT ANY DICTIONARY,但可通过SELECT_CATALOG_ROLE间接获得
查嵌套角色链里的 ANY 权限(不止一层)
一个角色 A 授予了角色 B,B 又授予了 C,而 C 才含 SELECT ANY TABLE ——只查直接授予会漏掉这种三层链。必须用 CONNECT BY 展开。
示例语句(以 APP_ADMIN 为起点):
SELECT DISTINCT r.role, s.privilege FROM (SELECT granted_role role FROM DBA_ROLE_PRIVS START WITH granted_role = 'APP_ADMIN' CONNECT BY PRIOR granted_role = granted_role) r JOIN ROLE_SYS_PRIVS s ON r.role = s.role WHERE s.privilege LIKE '%ANY%';
关键点:
-
START WITH granted_role = 'xxx'中的xxx必须是角色名,不是用户名;若填错成用户,结果为空且无提示 -
CONNECT BY PRIOR granted_role = granted_role是常见写错点,正确应为CONNECT BY PRIOR granted_role = grantee(查角色被谁授予)或反向(查角色授予了谁),此处需按实际路径调整 - 若遇到
ORA-01436: CONNECT BY loop,说明角色循环授权(如 A→B→A),加NOCYCLE并用CONNECT_BY_ISCYCLE标记
为什么 PUBLIC 出现 ANY 权限必须立刻处理
PUBLIC 是所有用户的默认角色。一旦它被授予 SELECT ANY TABLE,等于全库任意用户(包括应用连接池里的低权限账号)都能读所有表——这不是过度授权,是数据库裸奔。
执行:SELECT * FROM ROLE_SYS_PRIVS WHERE role = 'PUBLIC' AND privilege LIKE '%ANY%';
如果命中,不要试图“先评估影响”,直接 REVOKE SELECT ANY TABLE FROM PUBLIC;。延迟一秒,敏感数据就可能已被拉走。
容易被忽略的是:某些老系统初始化脚本或第三方工具(如旧版 Oracle Enterprise Manager)会悄悄给 PUBLIC 加权限,且不会出现在常规角色审计报告里——必须单独、显式查这一条。











