SELECT ANY TABLE 是危险的,因为它赋予用户查询所有 schema 所有表(含未来新建表)的系统级权限,且无法按表或 schema 精细回收,审计难追溯;正确做法是通过角色封装对象级 SELECT 权限,并显式授予 CREATE SESSION、锁定账号、单独授权视图/同义词等。
不能直接用 grant select any table,它会让用户查遍所有 schema 的所有表(包括未来新建的),且无法按表或 schema 回收,属于高危操作。
为什么 SELECT ANY TABLE 是危险的
它不是“只读授权”,而是系统级全局权限:一旦授予,用户能查 SCOTT.EMP、HR.DEPARTMENTS,甚至你还没建的表。更麻烦的是,REVOKE SELECT ANY TABLE 只能整权收回,没法说“收回对 CONFIG_SCHEMA 下表的访问”。审计时也难定位——DBA_SYS_PRIVS 里只记了一条记录,看不出实际影响范围。
正确做法:用角色封装对象级 SELECT 权限
核心是把权限收口到角色,再把角色给用户。后续增删表权限只需改角色,不影响用户本身。
- 先建角色:
CREATE ROLE app_readonly; - 生成授权语句(需 DBA 或目标 schema owner 执行):
SELECT 'GRANT SELECT ON ' || owner || '.' || table_name || ' TO app_readonly;' FROM dba_tables WHERE owner IN ('APP_SCHEMA', 'CONFIG_SCHEMA'); - 执行生成的每条
GRANT SELECT ON ... TO app_readonly;(注意不是ANY TABLE) - 最后赋角色:
GRANT app_readonly TO readonly_user;
别忘了登录权限和账号锁死
只读角色不等于能连库。用户没 CREATE SESSION 会报 ORA-01045;没锁账号,就可能被拿去连 SQL*Plus 手动查敏感视图。
- 必须显式授连接权:
GRANT CREATE SESSION TO readonly_user; - 生产环境应禁用交互登录:
ALTER USER readonly_user ACCOUNT LOCK; - 如需首次连接改密:
ALTER USER readonly_user PASSWORD EXPIRE;
视图、同义词、动态性能视图容易漏授权
对象权限不自动继承到视图或同义词。如果应用查 v$session 或自定义视图 vw_user_active,这些都得单独授 SELECT。
- 查
v$视图:不要授SELECT_CATALOG_ROLE(权限过大),改用封装视图:CREATE VIEW v_session_ro AS SELECT sid, username, program FROM v$session;
再GRANT SELECT ON v_session_ro TO app_readonly; - 同义词:必须对同义词指向的原对象(表/视图)授
SELECT,同义词本身不带权限 - 数据字典表(如
dba_tables):普通角色默认无权访问,显式授SELECT ANY DICTIONARY极不推荐
真正可控的只读,不是靠“禁止写”,而是靠“只开读的门”——门钥匙只配给明确需要的那几把,且每把钥匙只开一扇门。


















