select any table是高危系统级权限,可读取所有用户(含sys)任意表并用于提权;应严格按最小权限原则授具体对象权限或角色,误授后须及时回收并检查依赖对象。
select any table 是数据库里的“万能钥匙”
它不是普通权限,而是绕过对象级授权的系统级通行证——只要用户拥有这个权限,就能读取数据库中任意用户(包括 sys、system)下的任意表,哪怕那张表根本没显式授权给他。
更危险的是:它能被用来提权。比如创建一个指向 SYS 模式的存储过程,再用 EXECUTE IMMEDIATE 执行动态 SQL,就能悄悄执行 GRANT DBA TO xxx 这类操作。
- 常见错误现象:
SELECT * FROM dba_users突然能执行了,但你没给用户DBA角色 - 使用场景:仅限 DBA 日常巡检或紧急故障排查,且应通过临时会话 + 审计日志严格约束
- 参数差异:和
SELECT ANY DICTIONARY不同,前者能查所有业务表,后者只覆盖数据字典视图
误授后回收困难且风险残留
直接 REVOKE SELECT ANY TABLE FROM user_name 是最干净的做法,但有两个关键盲点:
- 该权限曾被用于创建跨 Schema 的高危对象(如
SYS下的存储过程),回收后这些对象仍存在并可被调用 - 如果当初是通过脚本批量授予的,很可能连谁被授了、为什么授都记不清了,导致回收不彻底
-
REVOKE不会自动清理下游依赖——比如用户用它创建了视图或物化视图,这些对象依然能访问敏感数据
回收前必查:SELECT object_name, object_type FROM dba_objects WHERE owner = 'SYS' AND created > SYSDATE - 7 AND status = 'VALID',重点看是否有新创建的 PROCEDURE 或 FUNCTION。
替代方案必须基于最小权限落地
绝大多数应用需求完全不需要 ANY 权限——它们真正需要的,只是对几个特定 Schema 下几张特定表的查询权。
- 正确做法:先确认目标对象,例如业务用户
APP_USER只需查HR.EMPLOYEES和HR.DEPARTMENTS,那就执行GRANT SELECT ON HR.EMPLOYEES TO APP_USER和GRANT SELECT ON HR.DEPARTMENTS TO APP_USER - 替代方案:如果涉及多个表且属同一 Schema,可建角色(如
hr_reader),批量授权后再授角色,便于后期回收 - 禁用交互登录:若只供应用连接,用
ALTER USER readonly_user PASSWORD EXPIRE+ALTER USER readonly_user ACCOUNT LOCK防止误用
PUBLIC 角色上绝不能碰 SELECT ANY TABLE
执行 REVOKE SELECT ANY TABLE FROM PUBLIC 会立即让数据库瘫痪——这不是加固,是自毁。
- 所有依赖数据字典视图的内置包(如
STANDARD、DBMS_STANDARD)立刻变INVALID - 后续任何 PL/SQL 编译、甚至用户登录都可能失败,典型错误是
ORA-06553: PLS-213: STANDARD package not accessible - 真要限制数据字典访问,应改用细粒度授权,比如只给特定用户查
V_$SESSION,而不是一刀切
真正难的不是“怎么 revoke”,而是判断哪些权限被谁用了、哪些能关、哪些得留着——这需要结合审计日志、应用清单和最小权限原则做交叉验证,而不是靠一条 SQL 解决所有问题。











