必须先查询确认public是否真实拥有utl_http等包的execute权限,再执行revoke;同时需检查dba_、all_视图的select权限,dba_sys_privs中的系统权限,以及public同义词和角色链式授权,避免遗漏高危权限。

查 PUBLIC 是否真有 UTL_HTTP、UTL_TCP 等 EXECUTE 权限
别凭印象或文档操作,先确认权限真实存在。很多人跳过这步,直接 REVOKE 结果报 ORA-01927(你没授过这个权),其实是白忙活。
- 执行:
SELECT grantee, privilege, owner, table_name FROM dba_tab_privs WHERE grantee = 'PUBLIC' AND privilege = 'EXECUTE' AND table_name IN ('UTL_HTTP', 'UTL_TCP', 'UTL_FILE', 'UTL_SMTP', 'DBMS_RANDOM', 'DBMS_LOB', 'DBMS_SQL'); - 注意大小写:
UTL_HTTP必须大写,视图里存的就是大写;漏写owner = 'SYS'可能漏掉记录(虽然多数情况默认是 SYS) - 如果结果为空,说明这些包的 EXECUTE 权限根本没授给 PUBLIC,不用撤
别漏查 DBA_* 视图上的 SELECT 权限
PUBLIC 对 DBA_USERS、DBA_TAB_PRIVS 这类视图的 SELECT 权限,等于把整个数据库的元数据暴露给所有用户——等保2.0明确禁止。
- 执行:
SELECT grantee, privilege, owner, table_name FROM dba_tab_privs WHERE grantee = 'PUBLIC' AND privilege = 'SELECT' AND owner = 'SYS' AND table_name LIKE 'DBA_%'; - 特别注意:
DBA_*视图本身不被 PUBLIC 默认拥有权限,但有些 DBA 手动授过,必须查实 -
ALL_*视图(如ALL_SOURCE)也常被误授,同样要扫一遍:AND table_name LIKE 'ALL_%'
系统权限不能只看 DBA_TAB_PRIVS
DBA_TAB_PRIVS 只管对象权限(比如对某张表、某个包的权限),而 PUBLIC 也可能被授予系统权限(如 CREATE SESSION),这类权限藏在 DBA_SYS_PRIVS 里。
- 执行:
SELECT grantee, privilege, admin_option FROM dba_sys_privs WHERE grantee = 'PUBLIC'; - 重点盯:
CREATE SESSION(通常允许,但需确认是否必要)、UNLIMITED TABLESPACE(高危,应收回)、SELECT ANY DICTIONARY(绝对禁止) - 如果查到
SELECT ANY DICTIONARY,立刻REVOKE SELECT ANY DICTIONARY FROM PUBLIC;—— 这比对象权限更危险,它让 PUBLIC 能读所有数据字典基表
同义词和角色链式授权容易被忽略
权限可能不是直接授给 PUBLIC,而是通过角色间接授予,或者靠 PUBLIC 同义词绕过权限检查。这两类问题不会出现在 DBA_TAB_PRIVS 的简单查询中。
- 查 PUBLIC 同义词:
SELECT synonym_name, table_owner, table_name FROM dba_synonyms WHERE owner = 'PUBLIC' AND table_owner = 'SYS' AND table_name IN ('UTL_HTTP', 'UTL_FILE'); - 查角色链式授权:先找 PUBLIC 拥有哪些角色:
SELECT granted_role FROM dba_role_privs WHERE grantee = 'PUBLIC';,再查这些角色是否被授了高危包的 EXECUTE 权限 - 哪怕
DBA_TAB_PRIVS查不到 PUBLIC 直接权限,只要存在上述任一路径,风险就真实存在
真正难的不是“怎么查”,而是判断哪些权限是业务必需、哪些只是历史遗留。一次 SELECT 返回几十行结果很常见,但其中可能只有 2–3 条是实际被调用的——得结合 DBA_DEPENDENCIES 和应用日志交叉验证,否则容易误伤。











