利用VPD细粒度访问策略实现行级安全性 Step By Step

Oracle8i以后的版本都提供了VPD(virtual private database 虚拟专用数据库)这样一个强大的功能来实现呼声日益增高的系统安全性要求。

通过设置基于VPD的细粒度访问策略,我们可以只通过DBA的工作(不需要修改应用,也就是应用透明化)就可以实现用户只能访问自己有权限访问的数据,当然如果需要更加复杂的权限控制开发人员的参与还是必不可少的。

下面用一个简单的例子来实现这样的功能,在EMP表中的用户登录数据库以后只能查询和更新自己所属部门的其它员工资料,不是本部门的不会显示也不允许更新。

1. 我们需要用到3个用户,一个是拥有EMP表的SCOTT用户,一个是用于设置VPD策略的KAMUS用户,另外一个是在EMP表中有记录的SMITH用户。
SCOTT用户可以利用$ORACLE_HOME/rdbms/admin/scott.sql创建。
@?/rdbms/admin/scott.sql
KAMUS用户需要能够执行DBMS_RLS包的权限。
grant execute on dbms_rls to kamus;
SMITH用户需要能够读取并更新EMP表的权限。
grant select on scott.emp to smith;

2. 首先用KAMUS用户创建策略权限表。为方便起见我们直接通过EMP表创建,本策略表中包含了员工姓名和所属部门编号。为了更加完善策略函数,我们创建完策略表后在表中新增一条记录,让SMITH用户同时属于20和10这两个部门。

SQL> create table access_policy as select ename,deptno from scott.emp;

Table created

SQL> select * from access_policy;

ENAME DEPTNO
———- ——
SMITH 20
ALLEN 30
WARD 30
JONES 20
MARTIN 30
BLAKE 30
CLARK 10
KING 10
TURNER 30
JAMES 30
FORD 20
MILLER 10

12 rows selected

SQL> insert into access_policy values(‘SMITH’,10);

1 row inserted

SQL> commit;

Commit complete

3. KAMUS用户创建VPD策略需要的函数。
create or replace function get_user_dept_id
(
p_schema in varchar2,
p_table in varchar2
)
return varchar2
as
l_retstr varchar2(2000);
begin
if (p_schema = user) then
l_retstr := null;
else
for user_rec in
(
select deptno
from access_policy
where ename = USER
) loop
l_retstr := l_retstr||’,’||user_rec.deptno;
end loop;
l_retstr := ltrim(l_retstr,’,’);
if (l_retstr is null) then
l_retstr := ‘1=2’;
else
l_retstr := ‘DEPTNO IN (‘||l_retstr||’)’;
end if;
end if;
return l_retstr;
end;

该函数实现以下功能:
如果使用SCOTT用户登录,因为表是属于该用户的,所以不加任何限制。
如果使用其它用户登录(SYS用户不受此限制),那么根据EMP表中该用户的所属部门决定哪些记录允许该用户操作,本例中10和20这两个部门的员工SMITH用户将都能看见。
如果登录的用户不在EMP表中,那么该用户查看不到任何数据,比如KAMUS用户。

注意:
VPD策略函数必须包含两个参数,本例中是p_schema和p_table,即使这两个参数在函数中没有用到,也必须包含。否则在后面检索EMP表数据的时候将会报:
PLS-00306: 调用 ‘GET_USER_DEPT_ID’ 时参数个数或类型错误。

我们用SMITH用户登录数据库来测试一下这个函数的返回值。

SQL> select kamus.get_user_dept_id(‘SCOTT’,’EMP’) from dual;

KAMUS.GET_USER_DEPT_ID(‘SCOTT’
——————————————————————————–
DEPTNO IN (20,10)

也就是说基于VPD的细粒度访问策略实际上就是在用户提交一个SQL以后会将策略函数返回值添加到where语句后面,通过这种方式来限制用户对于表中数据的访问。

4. 用KAMUS用户创建VPD策略。

begin
dbms_rls.add_policy (
object_schema => ‘SCOTT’,
object_name => ‘EMP’,
policy_name => ‘EMP_SEL_POLICY’,
function_schema => ‘KAMUS’,
policy_function => ‘GET_USER_DEPT_ID’,
statement_types => ‘SELECT’
);
end;
/

这是用于select的策略。

begin
dbms_rls.add_policy (
object_schema => ‘SCOTT’,
object_name => ‘EMP’,
policy_name => ‘EMP_IUD_POLICY’,
function_schema => ‘KAMUS’,
policy_function => ‘GET_USER_DEPT_ID’,
statement_types => ‘INSERT, UPDATE, DELETE’,
update_check => TRUE
);
end;
/

这是用于DML的策略。

5. 至此为止我们的VPD方案就已经设置完毕了。下面我们测试一下。

SQL> conn scott/tiger
已连接。
–SCOTT用户可以选择出全部的12条记录
SQL> select count(*) from emp;

COUNT(*)
———-
12

SQL> conn smith/smith
已连接。
–SMITH用户只能选择出属于10,20部门的6条记录
SQL> select count(*) from scott.emp;

COUNT(*)
———-
6
–更新也只能更新6行记录
SQL> update scott.emp set sal=sal+100;

已更新6行。
–如果尝试插入一个其它部门的员工将会报错
SQL> insert into scott.emp(empno,ename,deptno) values(9999,’VPD’,30);
insert into scott.emp(empno,ename,deptno) values(9999,’VPD’,30)
*
第 1 行出现错误:
ORA-28115: 策略违反检验选项
–即使指定了要更新30部门的数据也是一条都无法更新
SQL> update scott.emp set sal=sal+100 where deptno=30;

已更新0行。
–同样删除也只能删除6行数据
SQL> delete scott.emp;

已删除6行。

结论:
通过基于VPD的细粒度访问策略可以很简单地在数据库这个层面上完成对于应用数据的安全性保护,而如果通过SYS_CONTEXT以及数据库登录触发器来编写更加复杂的VPD策略函数的话,就能实现非常强大的数据安全性功能。

后续:
Oracle数据库安全领域有很多我还没有接触到的东西需要学习,下面一篇文章我将会介绍同样是基于VPD的更深一步的安全策略解决方案 - Label Security,敬请期待