Oracle Virtual Private Database(VPD)
Oracle Virtual Private Database(VPD)
适用版本:Oracle Database 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07
1. 概述
Virtual Private Database(VPD,虚拟专用数据库) 是 Oracle 的行级安全控制机制[1]:
核心特性:
- 行级访问控制:基于用户上下文过滤行
- 附加 WHERE 子句:自动添加到 SQL
- 细粒度安全:表/视图/列级
- 应用透明:无需修改 SQL
典型场景:
- 多租户数据隔离
- 部门数据隔离
- 行级权限控制
2. VPD 工作原理
2.1 工作流程
1. 用户执行 SQL: SELECT * FROM employees
2. VPD 策略触发
3. 策略函数返回谓词: dept_id = 10
4. SQL 改写: SELECT * FROM employees WHERE dept_id = 10
5. 执行改写后的 SQL
6. 仅返回 dept_id = 10 的行
2.2 组件
| 组件 | 作用 |
|---|---|
| Policy(策略) | 定义策略规则 |
| Policy Function | 返回谓词的函数 |
| Application Context | 上下文信息 |
| Fine-Grained Access Control | FGAC 实现 |
3. 配置 VPD
3.1 创建 Application Context
-- 创建 context
CREATE OR REPLACE CONTEXT dept_ctx USING dept_pkg;
-- 创建包
CREATE OR REPLACE PACKAGE dept_pkg IS
PROCEDURE set_dept_id;
END;
/
CREATE OR REPLACE PACKAGE BODY dept_pkg IS
PROCEDURE set_dept_id IS
v_dept_id NUMBER;
BEGIN
-- 根据用户名获取部门 ID
SELECT dept_id INTO v_dept_id
FROM user_dept
WHERE username = sys_context('USERENV', 'SESSION_USER');
-- 设置 context
DBMS_SESSION.SET_CONTEXT('dept_ctx', 'dept_id', v_dept_id);
END;
END;
/
3.2 创建登录触发器
-- 登录时自动设置 context
CREATE OR REPLACE TRIGGER set_dept_ctx_trg
AFTER LOGON ON DATABASE
BEGIN
dept_pkg.set_dept_id;
END;
/
3.3 创建策略函数
-- 策略函数返回谓词
CREATE OR REPLACE FUNCTION dept_policy_func(
schema_name IN VARCHAR2,
table_name IN VARCHAR2
) RETURN VARCHAR2 IS
v_dept_id VARCHAR2(100);
BEGIN
-- 获取 context 中的 dept_id
v_dept_id := sys_context('dept_ctx', 'dept_id');
-- 返回谓词
IF v_dept_id IS NULL THEN
RETURN '1=1'; -- 无限制
ELSE
RETURN 'dept_id = ' || v_dept_id;
END IF;
END;
/
3.4 添加策略
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'scott',
object_name => 'employees',
policy_name => 'dept_policy',
function_schema => 'sys',
policy_function => 'dept_policy_func',
statement_types => 'select, insert, update, delete',
update_check => TRUE,
enable => TRUE
);
END;
/
3.5 测试
-- 用户 alice(dept_id=10)查询
SELECT * FROM scott.employees;
-- 实际执行: SELECT * FROM scott.employees WHERE dept_id = 10
-- 仅返回 dept_id=10 的行
-- 用户 bob(dept_id=20)查询
SELECT * FROM scott.employees;
-- 仅返回 dept_id=20 的行
4. 策略类型
4.1 动态策略(默认)
BEGIN
DBMS_RLS.ADD_POLICY(
...
policy_type => DBMS_RLS.DYNAMIC
);
END;
/
-- 每次执行都调用策略函数
4.2 静态策略
BEGIN
DBMS_RLS.ADD_POLICY(
...
policy_type => DBMS_RLS.STATIC
);
END;
/
-- 策略函数只调用一次,结果缓存
4.3 共享静态策略
BEGIN
DBMS_RLS.ADD_POLICY(
...
policy_type => DBMS_RLS.SHARED_STATIC
);
END;
/
-- 多个对象共享策略
4.4 上下文敏感策略
BEGIN
DBMS_RLS.ADD_POLICY(
...
policy_type => DBMS_RLS.CONTEXT_SENSITIVE
);
END;
/
-- context 变化时重新执行
4.5 共享上下文敏感策略
BEGIN
DBMS_RLS.ADD_POLICY(
...
policy_type => DBMS_RLS.SHARED_CONTEXT_SENSITIVE
);
END;
/
5. 列级 VPD
5.1 列级策略
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'scott',
object_name => 'employees',
policy_name => 'salary_policy',
function_schema => 'sys',
policy_function => 'salary_policy_func',
sec_relevant_cols => 'salary,bonus', -- 敏感列
sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS -- 显示所有行但敏感列置 NULL
);
END;
/
5.2 策略函数
CREATE OR REPLACE FUNCTION salary_policy_func(
schema_name IN VARCHAR2,
table_name IN VARCHAR2
) RETURN VARCHAR2 IS
BEGIN
-- 仅 HR 经理可查看薪资
IF sys_context('USERENV', 'SESSION_USER') = 'HR_MANAGER' THEN
RETURN NULL; -- 无限制
ELSE
RETURN '1=0'; -- 敏感列置 NULL
END IF;
END;
/
6. 策略管理
6.1 查看策略
SELECT
object_owner,
object_name,
policy_name,
function_schema,
policy_function,
policy_type,
enable
FROM dba_policies;
6.2 启用/禁用策略
-- 禁用
BEGIN
DBMS_RLS.ENABLE_POLICY(
object_schema => 'scott',
object_name => 'employees',
policy_name => 'dept_policy',
enable => FALSE
);
END;
/
-- 启用
BEGIN
DBMS_RLS.ENABLE_POLICY(
object_schema => 'scott',
object_name => 'employees',
policy_name => 'dept_policy',
enable => TRUE
);
END;
/
6.3 删除策略
BEGIN
DBMS_RLS.DROP_POLICY(
object_schema => 'scott',
object_name => 'employees',
policy_name => 'dept_policy'
);
END;
/
6.4 刷新策略
-- 刷新所有策略
EXEC DBMS_RLS.REFRESH_GROUPED_POLICY;
-- 刷新指定策略
BEGIN
DBMS_RLS.REFRESH_POLICY(
object_schema => 'scott',
object_name => 'employees',
policy_name => 'dept_policy'
);
END;
/
7. 分组策略
7.1 创建分组策略
BEGIN
DBMS_RLS.CREATE_POLICY_GROUP(
object_schema => 'scott',
object_name => 'employees',
policy_group => 'hr_pg'
);
END;
/
-- 添加策略到分组
BEGIN
DBMS_RLS.ADD_GROUPED_POLICY(
object_schema => 'scott',
object_name => 'employees',
policy_group => 'hr_pg',
policy_name => 'dept_policy',
function_schema => 'sys',
policy_function => 'dept_policy_func'
);
END;
/
7.2 启用分组
-- 设置启用的分组
BEGIN
DBMS_RLS.ENABLE_GROUPED_POLICY(
object_schema => 'scott',
object_name => 'employees',
group_ctx => 'hr_pg'
);
END;
/
8. 多租户与 VPD
8.1 PDB 中的 VPD
-- 切换到 PDB
ALTER SESSION SET CONTAINER = hrpdb;
-- 创建 VPD 策略(同上)
-- VPD 仅在当前 PDB 生效
8.2 Common VPD
VPD 策略不能跨 PDB,每个 PDB 独立配置。
9. 监控 VPD
9.1 策略查询
SELECT * FROM dba_policies WHERE object_name='EMPLOYEES';
9.2 Context 查询
-- 查看 context
SELECT namespace, attribute, value
FROM session_context
WHERE namespace='DEPT_CTX';
9.3 执行计划
-- 查看实际 SQL
EXPLAIN PLAN FOR SELECT * FROM scott.employees;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 可见 WHERE 子句包含 VPD 谓词
10. 常见坑与排错
10.1 策略不生效
修复:
-- 1. 检查策略是否启用
SELECT enable FROM dba_policies WHERE policy_name='DEPT_POLICY';
-- 2. 启用策略
BEGIN
DBMS_RLS.ENABLE_POLICY(
object_schema => 'scott',
object_name => 'employees',
policy_name => 'dept_policy',
enable => TRUE
);
END;
/
-- 3. 检查 context
SELECT * FROM session_context WHERE namespace='DEPT_CTX';
-- 4. 检查策略函数
SELECT dept_policy_func('scott','employees') FROM dual;
10.2 ORA-28113: 策略谓词错误
修复:
-- 1. 检查策略函数返回值
SELECT dept_policy_func('scott','employees') FROM dual;
-- 2. 确保返回有效的 SQL 谓词
-- 例如: 'dept_id = 10'
10.3 Context 未设置
修复:
-- 1. 检查登录触发器
SELECT trigger_name, status FROM dba_triggers WHERE trigger_name='SET_DEPT_CTX_TRG';
-- 2. 手动设置
EXEC dept_pkg.set_dept_id;
-- 3. 检查 context
SELECT sys_context('dept_ctx', 'dept_id') FROM dual;
10.4 SYS 用户绕过 VPD
原因:SYS 用户默认绕过 VPD。
修复:
-- VPD 不影响 SYS/SYSDBA
-- 需要 VPD 的用户不能是 SYS
10.5 性能问题
修复:
-- 1. 使用静态策略
BEGIN
DBMS_RLS.ADD_POLICY(
...
policy_type => DBMS_RLS.STATIC
);
END;
/
-- 2. 确保谓词列有索引
CREATE INDEX idx_emp_dept ON employees(dept_id);
-- 3. 监控策略执行
11. 最佳实践
- 使用 Application Context:避免每次查询
- 登录触发器设置 context:自动化
- 策略列加索引:提升性能
- 静态策略优先:性能更好
- 列级 VPD:敏感数据保护
- 分组策略:复杂场景
- 监控策略执行:dba_policies
- 避免 SYS 用户:SYS 绕过 VPD
- PDB 中独立配置:多租户隔离
- 结合 Fine-Grained Auditing:审计
12. 参考资料
[1] Oracle Database Security Guide 19c, “Using Oracle Virtual Private Database” https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/using-oracle-virtual-private-database.html