只查 DBA_SYS_PRIVS 不够可靠,因它仅记录直接授予的系统权限,不包含通过角色继承的权限;需结合 DBA_ROLE_PRIVS 递归展开角色链并关联 DBA_SYS_PRIVS 才能查全所有拥有 DROP ANY TABLE 权限的用户。
直接查 dba_sys_privs 视图就能定位,但必须注意权限来源是直接授予还是通过角色继承——后者容易漏查。
为什么只查 DBA_SYS_PRIVS 不够可靠
用户可能没被直接授予 DROP ANY TABLE,而是通过角色(比如 DBA、EXP_FULL_DATABASE)间接获得。这类权限不会出现在 DBA_SYS_PRIVS 的 GRANTEE 字段里,而是在 DBA_ROLE_PRIVS 链路中层层展开。
常见错误是只运行:SELECT GRANTEE FROM DBA_SYS_PRIVS WHERE PRIVILEGE = 'DROP ANY TABLE';
这会漏掉所有靠角色继承该权限的用户。
- 系统权限不级联:A 授予 B 角色,B 授予 C 用户,C 不自动继承 A 的系统权限
- 但角色可嵌套:DBA 角色本身包含
DROP ANY TABLE,只要用户被授了 DBA,就拥有该权限 -
DBA_SYS_PRIVS只记录“直接授予”,不反映角色链路
查全所有拥有者:用 CONNECT BY 展开角色继承链
核心思路是把角色授予关系递归展开,再关联到角色所含的系统权限。以下 SQL 能覆盖直接授予 + 所有层级角色继承:
SELECT DISTINCT r.username, s.privilege FROM ( SELECT DISTINCT CONNECT_BY_ROOT grantee AS username, granted_role FROM DBA_ROLE_PRIVS START WITH grantee IN (SELECT username FROM DBA_USERS) CONNECT BY PRIOR granted_role = grantee ) r JOIN DBA_SYS_PRIVS s ON r.granted_role = s.grantee WHERE s.privilege = 'DROP ANY TABLE' ORDER BY r.username;
说明:
-
CONNECT BY PRIOR granted_role = grantee向上追溯每个角色被谁授予,直到根用户 -
START WITH grantee IN (SELECT username FROM DBA_USERS)确保起点是真实用户,不是中间角色 - 结果中
username是最终拥有权限的用户,privilege是具体权限名
DROP ANY TABLE 权限的实际风险点
这个权限极危险,它允许删除任意用户下的任意表(除 SYS 拥有的核心对象外),且无法按 schema 或对象粒度限制。一旦误用或被滥用,可能导致生产数据不可逆丢失。
- 即使用户没登录过,只要权限存在,就构成潜在风险
-
WITH ADMIN OPTION更要警惕:被授权者还能把该权限转授他人 - 检查时顺便确认:
SELECT GRANTEE, ADMIN_OPTION FROM DBA_SYS_PRIVS WHERE PRIVILEGE = 'DROP ANY TABLE'; - 多租户环境下,还需过滤
COMMON = 'YES'(仅适用于 CDB 共享权限)
快速验证某个用户是否真能执行 DROP ANY TABLE
光看授权记录不够,得确认当前 session 是否激活了对应角色:
SELECT * FROM SESSION_PRIVS WHERE PRIVILEGE = 'DROP ANY TABLE';
这条语句返回空,不代表权限不存在,只说明当前 session 没激活含该权限的角色(比如角色被 ALTER USER ... DEFAULT ROLE NONE 禁用了)。真正生效需满足两个条件:
- 权限已授予(查
DBA_SYS_PRIVS或角色链) - 对应角色在用户 session 中处于激活状态(查
SESSION_ROLES)
角色是否默认激活,取决于创建时的 DEFAULT ROLE 设置和后续 ALTER USER 操作——这点常被忽略,导致“明明授了权却执行失败”。


















