应立即执行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','DBMS_RANDOM','DBMS_LOB','DBMS_SQL');核查高危包执行权限,同时补查owner!='SYS'及PUBLIC同义词,并确认当前用户具备SELECT ANY DICTIONARY权限以保障查询完整性。

查 PUBLIC 上有哪些 EXECUTE 权限(重点盯高危包)
Oracle 中 PUBLIC 被授予 EXECUTE 权限,等于把对应包的调用权默认开放给所有用户,这是等保和安全审计的高频扣分项。必须立刻确认是否存在对 UTL_HTTP、UTL_TCP、UTL_FILE、DBMS_RANDOM、DBMS_LOB、DBMS_SQL 等包的授权。
执行以下语句查实:
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', 'DBMS_RANDOM', 'DBMS_LOB', 'DBMS_SQL');- 如果返回结果为空,不代表绝对安全:有些 DBA 会把包放在非
SYSschema 下并授予权限,所以建议补查owner != 'SYS'的情况; -
table_name大小写不敏感,但查询时统一用大写更稳妥,避免漏匹配; - 别忽略同义词干扰:即使没直接授
EXECUTE ON SYS.UTL_HTTP,也可能存在CREATE PUBLIC SYNONYM UTL_HTTP FOR SYS.UTL_HTTP,需同步查dba_synonyms。
查 PUBLIC 是否被授予 DBA_* 或 ALL_* 视图的 SELECT 权限SELECT ANY DICTIONARY 是系统级高危权限,但很多人会绕过它,直接把 DBA_USERS、DBA_TAB_PRIVS、DBA_OBJECTS 等视图的 SELECT 权限授给 PUBLIC,效果类似——任何用户都能窥探数据字典。
运行这个语句定位风险视图:
SELECT grantee, privilege, owner, table_name
FROM dba_tab_privs
WHERE grantee = 'PUBLIC'
AND privilege = 'SELECT'
AND (owner = 'SYS' AND table_name LIKE 'DBA_%')
OR table_name IN ('ALL_USERS', 'ALL_TAB_PRIVS', 'ALL_OBJECTS');- 注意:不是所有
DBA_*视图都默认被PUBLIC拥有权限,但一旦手动授予,就构成严重越权; -
ALL_*视图虽只显示“当前用户可访问的对象”,但若PUBLIC拥有,意味着任意用户都能枚举其他用户的对象结构,为 SQL 注入或横向探测提供便利; - 若发现
GRANTABLE = 'YES',说明该权限还能被再转授,风险翻倍,必须优先处理。
为什么查不到权限却仍有风险?检查权限视图访问权限本身
你用 DBA_TAB_PRIVS 查不到记录,不等于权限不存在——更可能是你当前用户没权限看。
常见原因包括:
- 当前用户未被授予
SELECT ANY DICTIONARY或SELECT_CATALOG<em>ROLE</em>,导致无法访问DBA*视图; - 误用
USER_TAB_PRIVS(只查自己被授的权限)或ALL_TAB_PRIVS(只查自己有访问权的对象),而没切到SYS或具备相应角色的账号下执行; -
OWNER条件写错:比如查SCOTT.EMP时写了OWNER = 'scott'(小写),而数据字典中存的是大写'SCOTT',导致过滤失败; - 某些权限是通过角色间接授予
PUBLIC的(例如先GRANT ROLE_X TO PUBLIC,再GRANT EXECUTE ON UTL_HTTP TO ROLE_X),这种不会出现在DBA_TAB_PRIVS,需结合ROLE_TAB_PRIVS追踪。
查完之后别急着 revoke,先看谁在用
撤销 PUBLIC 权限后,已有编译成功的 PL/SQL 对象(如 PROCEDURE、PACKAGE)仍能运行,直到被重新编译或失效重编译——这意味着问题不会立刻暴露,而可能在凌晨三点定时任务触发时崩掉。
必须做三件事:
- 查依赖:运行
SELECT * FROM dba_dependencies WHERE referenced_name IN ('UTL_HTTP', 'DBMS_RANDOM') AND referenced_owner = 'SYS';,找出哪些对象调用了这些包; - 查状态:
SELECT owner, object_name, object_type, status FROM dba_objects WHERE status = 'INVALID';,确认撤销后是否已产生无效对象; - 查作业:
SELECT job_name, state, last_start_date, next_run_date FROM dba_scheduler_jobs WHERE enabled = 'TRUE' AND job_action LIKE '%UTL%';,防止定时任务静默失败。
真正麻烦的从来不是“能不能 revoke”,而是“revoke 后谁会突然哑火”。生产环境里,没人值班的凌晨三点,才是权限回收最真实的考场。


















