Dec 24 2006

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

Published by kamus at 2:23 am under Oracle RDBMS

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这两个部门。

  1. SQL> create table access_policy as select ename,deptno from scott.emp;
  2.  
  3. Table created
  4.  
  5. SQL> select * from access_policy;
  6.  
  7. ENAME      DEPTNO
  8. ---------- ------
  9. SMITH          20
  10. ALLEN          30
  11. WARD           30
  12. JONES          20
  13. MARTIN         30
  14. BLAKE          30
  15. CLARK          10
  16. KING           10
  17. TURNER         30
  18. JAMES          30
  19. FORD           20
  20. MILLER         10
  21.  
  22. 12 rows selected
  23.  
  24. SQL> insert into access_policy values('SMITH',10);
  25.  
  26. 1 row inserted
  27.  
  28. SQL> commit;
  29.  
  30. Commit complete

3. KAMUS用户创建VPD策略需要的函数。

  1. create or replace function get_user_dept_id
  2. (
  3.    p_schema in varchar2,
  4.    p_table in varchar2
  5. )
  6.    return varchar2
  7. as
  8.    l_retstr varchar2(2000);
  9. begin
  10.    if (p_schema = user) then
  11.       l_retstr := null;
  12.    else
  13.       for user_rec in
  14.       (
  15.          select deptno
  16.          from access_policy
  17.          where ename = USER
  18.       ) loop
  19.          l_retstr := l_retstr||','||user_rec.deptno;
  20.       end loop;
  21.       l_retstr := ltrim(l_retstr,',');     
  22.       if (l_retstr is null) then
  23.          l_retstr := '1=2';
  24.       else
  25.           l_retstr := 'DEPTNO IN ('||l_retstr||')';
  26.       end if;
  27.    end if;
  28.    return l_retstr;
  29. end;

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

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

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

  1. SQL> select kamus.get_user_dept_id('SCOTT','EMP') from dual;
  2.  
  3. KAMUS.GET_USER_DEPT_ID('SCOTT'
  4. --------------------------------------------------------------------------------
  5. DEPTNO IN (20,10)

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

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

  1. begin
  2. dbms_rls.add_policy (
  3.     object_schema    => 'SCOTT',
  4.     object_name      => 'EMP',
  5.     policy_name      => 'EMP_SEL_POLICY',
  6.     function_schema  => 'KAMUS',
  7.     policy_function  => 'GET_USER_DEPT_ID',
  8.     statement_types  => 'SELECT'
  9.  );
  10. end;
  11. /

这是用于select的策略。

  1. begin
  2. dbms_rls.add_policy (
  3.     object_schema    => 'SCOTT',
  4.     object_name      => 'EMP',
  5.     policy_name      => 'EMP_IUD_POLICY',
  6.     function_schema  => 'KAMUS',
  7.     policy_function  => 'GET_USER_DEPT_ID',
  8.     statement_types  => 'INSERT, UPDATE, DELETE',
  9.     update_check     => TRUE
  10.  );
  11. end;
  12. /

这是用于DML的策略。

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

  1. SQL> conn scott/tiger
  2. 已连接。
  3. --SCOTT用户可以选择出全部的12条记录
  4. SQL> select count(*) from emp
  5.  
  6.   COUNT(*)
  7. ----------
  8.         12
  9.  
  10. SQL> conn smith/smith
  11. 已连接。
  12. --SMITH用户只能选择出属于1020部门的6条记录
  13. SQL> select count(*) from scott.emp
  14.  
  15.   COUNT(*)
  16. ----------
  17.          6
  18.     --更新也只能更新6行记录
  19. SQL> update scott.emp set sal=sal+100;
  20.  
  21. 已更新6行。
  22. --如果尝试插入一个其它部门的员工将会报错
  23. SQL> insert into scott.emp(empno,ename,deptno) values(9999,'VPD',30);
  24. insert into scott.emp(empno,ename,deptno) values(9999,'VPD',30)
  25.                   *
  26. 1 行出现错误:
  27. ORA-28115: 策略违反检验选项
  28. --即使指定了要更新30部门的数据也是一条都无法更新
  29. SQL> update scott.emp set sal=sal+100 where deptno=30;
  30.  
  31. 已更新0行。
  32. --同样删除也只能删除6行数据
  33. SQL> delete scott.emp;
  34.  
  35. 已删除6行。

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

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

4 Responses to “利用VPD细粒度访问策略实现行级安全性 Step By Step”

  1. dido Says:

    以后用户会越来越关注安全问题。
    Kamus有没有留意到Oracle Database Vault 这个新特性? 它能防止 DBA 查看应用程序数据,解决了必须保护涉及合作伙伴、员工和顾客的敏感业务信息或隐私数据的客户最为担心的问题。
    我没有机会用,希望什么时候能看到Kamus的测试结果。

  2. dido Says:

    VPD能控制普通用户,但不能控制DBA

  3. dido Says:

    by the way,请教一个问题:
    听说你们consulting团队有人给客户实施过logical standby,想咨询现网使用的情况,是否稳定等等。方便的话能和我邮件联系吗?谢谢。

  4. kamus Says:

    to dido
    Oracle Database Vault的测试正在计划ing,因为目前只有linux和solaris版本发布,所以需要先安装一个for linux的db在自己的机器上。
    Label Security昨天已经测试完毕,正在整理文章。

    是有同事实施过DG,但是据说仍然是physical standby,我以前的客户自己实施过logical standby,目前一直在使用,有时候出现主备数据不一致的情况需要手工进行错误处理,其它的倒没听到什么不好。

Trackback URI | Comments RSS


Leave a Reply

-> :( :! :D :| :? :X ;;) :) ;)