GRANT SELECT ANY TABLE 是高危系统级权限,可读取所有用户(含SYS)的任意表并用于提权;应严格遵循最小权限原则,按需授予具体对象权限或角色,误授后须及时回收并检查依赖对象。
为什么 GRANT SELECT ANY TABLE 是数据库里的“万能钥匙”
它不是普通权限,而是绕过对象级授权的系统级通行证——只要用户拥有这个权限,就能读取数据库中任意用户(包括 sys、system)下的任意表,哪怕那张表根本没显式授权给他。
更关键的是:它还能被用来提权。比如创建一个指向 SYS 模式的存储过程,再用 EXECUTE IMMEDIATE 执行动态 SQL,就能悄悄执行 GRANT DBA TO ... 这类操作,把普通用户变成管理员。
- 常见错误现象:
SELECT * FROM dba_users突然能执行了,但你没给用户DBA角色 - 使用场景:仅限 DBA 日常巡检或紧急故障排查,且应通过临时会话+审计日志严格约束
- 参数差异:和
SELECT ANY DICTIONARY不同,前者能查所有业务表,后者只覆盖数据字典视图
授予权限时怎么避免掉进 ANY 权限陷阱
最小权限不是口号,是必须落地的操作原则。绝大多数应用需求完全不需要 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),批量授权后再授角色,便于后期回收 - 容易踩的坑:用脚本批量授
SELECT ANY TABLE应对“开发说连不上表”,结果让测试账号拥有了读取加密密钥表、审计日志表的能力
已经误授了 SELECT ANY TABLE,怎么安全回收
直接 REVOKE SELECT ANY TABLE FROM user_name 是最干净的做法,但要注意两点:一是确认该用户没有正在运行的依赖此权限的作业;二是检查是否曾用它创建过高危对象(如前面提到的跨 Schema 存储过程)。
- 回收前必查:
SELECT object_name, object_type FROM dba_objects WHERE owner = 'SYS' AND created > SYSDATE - 7 AND status = 'VALID',重点看是否有新创建的PROCEDURE或FUNCTION - 回收命令本身很简单:
REVOKE SELECT ANY TABLE FROM APP_USER - 风险点:如果该权限曾被用于授予其他用户(即带
WITH ADMIN OPTION),回收时不会自动级联撤销下游权限,得手动清理
SELECT ANY TABLE 和审计、监控之间的实际矛盾
很多 DBA 说“不给这个权限,就看不到慢 SQL 访问了哪些表”,但这其实是监控工具配置偏差导致的误解。现代 AWR/ASH 报告、SQL Monitor 视图(如 V$SQL_PLAN)本身不依赖用户级 SELECT ANY TABLE,而是靠 Oracle 内部机制采集元数据。
- 真正需要的权限是
SELECT_CATALOG_ROLE或细粒度授予SELECTonV_$SQL、V_$SQL_PLAN等动态性能视图 - 混淆点:某些老旧监控脚本硬编码了
SELECT * FROM all_tables,其实应改用DBA_TABLES+ 对应的系统权限,而非放行全部用户表 - 复杂点在于:权限回收后,应用连接池里可能还缓存着旧会话,得配合重启连接池或等待连接超时,否则权限变更不会立即生效

















