必须手动逐条执行revoke撤销public的高危权限,禁用cascade;高危权限包括execute on utl_http/utl_tcp等系统包,以及select on dba_users等数据字典视图,且需检查grantable='yes'记录并同步排查间接授权与失效对象。

直接回收 PUBLIC 的高危权限必须手动逐条执行 REVOKE,不能用 CASCADE,也不能依赖“一键清理”脚本——否则会报 ORA-01957: illegal option for REVOKE。
哪些权限算高危,默认就该回收
重点盯住两类组合:
-
EXECUTE权限 + 系统包名:如UTL_HTTP、UTL_TCP、UTL_FILE、DBMS_LOB、DBMS_RANDOM、DBMS_SQL -
SELECT权限 + 数据字典视图:特别是DBA_USERS、DBA_TAB_PRIVS、DBA_SYS_PRIVS等以DBA_开头的视图 - 额外注意:
GRANTABLE = 'YES'的记录——说明 PUBLIC 成员还能把这权限再转授出去,等保2.0明令禁止
查法固定用这两条:
SELECT grantee, privilege, owner, table_name
FROM dba_tab_privs
WHERE grantee = 'PUBLIC'
AND privilege IN ('EXECUTE','SELECT')
AND (table_name IN ('UTL_HTTP','UTL_TCP','UTL_FILE','DBMS_LOB','DBMS_RANDOM','DBMS_SQL')
OR (owner = 'SYS' AND table_name LIKE 'DBA_%'));
SELECT grantee, privilege, admin_option FROM dba_sys_privs WHERE grantee = 'PUBLIC';
REVOKE 语句怎么写才不报错
语法严格,漏一个词或加多余选项都会失败:
- 必须用
SYS或拥有GRANT ANY OBJECT PRIVILEGE的用户执行 - 对象权限用
REVOKE EXECUTE ON UTL_HTTP FROM PUBLIC;—— 不带CASCADE,不带引号,大小写不敏感但建议统一小写 - 系统权限用
REVOKE SELECT ANY DICTIONARY FROM PUBLIC;—— 同样不能加CASCADE - 如果查出
GRANTABLE = 'YES'的记录,必须先REVOKE ... FROM PUBLIC,再检查是否还有用户通过 PUBLIC 间接获得了该权限(这种链式授权不会出现在DBA_TAB_PRIVS中)
撤销后为什么代码还能跑,但半夜崩了
权限回收是立即生效的,但已有 PL/SQL 对象(PROCEDURE、FUNCTION、PACKAGE)不会自动失效:
- 已编译成功的对象仍可运行,直到下次被重新编译(比如修改源码、
ALTER PACKAGE ... COMPILE) - 这时会报
PLS-00201: identifier must be declared或ORA-00904: invalid identifier - 定时任务(
DBA_SCHEDULER_JOBS)可能凌晨三点触发,调用到含UTL_HTTP的过程,直接失败且无告警 - 所以必须立刻查:
SELECT object_name, object_type FROM dba_objects WHERE status = 'INVALID' AND owner NOT IN ('SYS','SYSTEM');
别只查 DBA_TAB_PRIVS 就以为完事了
很多人扫完 DBA_TAB_PRIVS WHERE GRANTEE = 'PUBLIC' 没结果,就认为“没风险”,这是最危险的误判:
- 权限可能通过角色间接授予 PUBLIC,例如:
GRANT SELECT ON emp TO hr_role; GRANT hr_role TO PUBLIC;—— 这种在DBA_TAB_PRIVS里查不到,因为GRANTEE是hr_role,不是PUBLIC -
PUBLIC不是普通角色,它不出现在DBA_ROLE_PRIVS或DBA_USERS里,只在权限授予目标字段中“露脸” - 合规检查必须同时跑两遍:
DBA_TAB_PRIVS和DBA_SYS_PRIVS,缺一不可
真正麻烦的从来不是 revoke 这一行命令,而是得顺着每一条被撤的权限,去翻应用日志、查失效对象、盯调度作业——这些活没法自动化,得人盯。











