EnterpriseDB’s Virtual Private Database vs oracle’s DBMS_RLS

Oracle的dbms_rls实现数据访问控制
在大部份系统中,权限控制主要定义为模块进入权限的控制和数据列访问权限的控制(如:某某人可以进入某个控制,仓库不充许查看有关部门的字段等等)。

但在某些系统中,权限控制又必须定义到数据行访问权限的控制,此需求一般出现在同一系统,不同的相对独立机构使用的情况。(如:集团下属多个子公司,所有子公司使用同一套数据表,但不同子公司的数据相对隔离),绝大多数人会选择在View加上Where子句来进行数据隔离。此方法编码工作量大、系统适应用户管理体系的弹性空间较小,一旦权限逻辑发生变动,就可能需要修改权限体系,导致所有的View都必须修改。

本文讨论EnterpriseDB的VPD,和oracle的dbms_rls有同等功能。
DBML_RLS包在数据库表上实现了虚拟私有库VPD(Virtual Private Database)。
VPD是一种细粒度访问控制安全策略,其是带有入参并有返回值的PL函数。
使用VPD的优点:
  a 细粒度安全
  b 在增、删、查、改sql语句上提供不同的安全策略。
  c 安全策略对不同session中的每一个sql语句可变。
  d 安全策略对于应用程序是透明的,不必修改应用就可以应用安全策略。
  e 一旦使能了安全策略,任何应用、包括新应用都不会绕过,甚至超级用户都不可能绕过,
  除非EXEMPT ACCESS POLICY系统权限grant给了用户,这个用户将能绕过数据库里所有的策略。


DBMS_RLS包提供存储过程做创建、删除、使能、使不能策略。
实现VPD的过程如下:
    a 创建一个策略函数。其有两个varchar2参数,并返回varchar2类型值,一个是schema名称,一个是数据库名称。
        where子句里有条件语句以过滤掉部分记录
    b 使用存储过程ADD_POLICY增加新策率,用这个存储过程还可以指明SQL命令的类型(增、删、查、改),是否在创建时使能,策略是否应用到新插入的行等。
    c 使用存储过程ENABLE_POLICY使能或使不能已有策略。
    d 使用存储过程DROP_POLICY删除现有策略。其不删除相关函数或者相关数据库对象。

创建的策略可以在兼容oracle的试图ALL_POLICIES、DBA_POLICIES、USER_POLICIES中查看。

例子:
基于PPAS带的例子中的emp。
CREATE TABLE public.vpemp AS SELECT empno, ename, job, sal, comm, deptno FROM emp;
ALTER TABLE vpemp ADD authid VARCHAR2(12);
UPDATE vpemp SET authid = 'researchmgr' WHERE deptno = 20;
UPDATE vpemp SET authid = 'salesmgr' WHERE deptno = 30;
SELECT * FROM vpemp;
empno | ename | job | sal | comm | deptno | authid
-------+--------+-----------+---------+---------+--------+-------------
7782 | CLARK | MANAGER | 2450.00 | | 10 |
7839 | KING | PRESIDENT | 5000.00 | | 10 |
7934 | MILLER | CLERK | 1300.00 | | 10 |
7369 | SMITH | CLERK | 800.00 | | 20 | researchmgr
7566 | JONES | MANAGER | 2975.00 | | 20 | researchmgr
7788 | SCOTT | ANALYST | 3000.00 | | 20 | researchmgr
7876 | ADAMS | CLERK | 1100.00 | | 20 | researchmgr
7902 | FORD | ANALYST | 3000.00 | | 20 | researchmgr
7499 | ALLEN | SALESMAN | 1600.00 | 300.00 | 30 | salesmgr
7521 | WARD | SALESMAN | 1250.00 | 500.00 | 30 | salesmgr
7654 | MARTIN | SALESMAN | 1250.00 | 1400.00 | 30 | salesmgr
7698 | BLAKE | MANAGER | 2850.00 | | 30 | salesmgr
7844 | TURNER | SALESMAN | 1500.00 | 0.00 | 30 | salesmgr
7900 | JAMES | CLERK | 950.00 | | 30 | salesmgr
(14 rows)
CREATE ROLE salesmgr WITH LOGIN PASSWORD 'salesmgr';
GRANT ALL ON vpemp TO salesmgr;

1
以superuser执行ADD_POLICY
ADD_POLICY(object_schema VARCHAR2, object_name VARCHAR2,
policy_name VARCHAR2, function_schema VARCHAR2,
policy_function VARCHAR2
[, statement_types VARCHAR2
[, update_check BOOLEAN
[, enable BOOLEAN
[, static_policy BOOLEAN
[, policy_type INTEGER
[, long_predicate BOOLEAN
[, sec_relevant_cols VARCHAR2
[, sec_relevant_cols_opt INTEGER ]]]]]]]])

object_schema:策略作用于的数据库对象做在的schema名字。
object_name:策略作用于数据库对象的名字。
policy_name:策略的名字
function_schema:包含策略函数的schema的名字。
policy_function:定义安全策略的函数名字
[, statement_types:SQL命令类型,默认是insert,update,delete,select,可以接受index但不起作用
[, update_check:进用于insert和update SQL命令,默认是false。如果根据策略函数谓词没有选定行,insert或update命令会抛异常,命令不影响任何行。
[, enable:策略使能,默认true
[, static_policy:true时,数据库服务器实例运行时评估一次,false时,策略谓词串在调用时被重新评估,默认false
                                    ppas上都是false,忽略该参数设置。
[, policy_type:NULL, ppas上NULL,忽略该参数设置。
[, long_predicate:限制 谓词程度,ppas忽略该参数设置。
[, sec_relevant_cols:逗号分隔的object_name的列。对列出的列提供列级VPD。
                                            默认是NULL,表示object_name的所有列都包含在该参数里。
[, sec_relevant_cols_opt:ppas不支持该参数,如果设置为oracle的DBMS_RLS.ALL_ROWS,ppas会抛一个异常。




例子1,行的例子
1)
CREATE OR REPLACE FUNCTION verify_session_user (
    p_schema VARCHAR2,
    p_object VARCHAR2
)
RETURN VARCHAR2
IS
BEGIN
    RETURN 'authid = SYS_CONTEXT(''USERENV'', ''SESSION_USER'')';
END;
上面函数里的谓词authid = SYS_CONTEXT('USERENV', 'SESSION_USER')会追加到存储过程
ADD_POLICY里指明的sql命令的where子句里. 这限制sql命令作用到了表vpemp的列authid是相同值的行.

2)
创建策略:
下面的匿名块调用存储过程ADD_POLICY,创建名字为secure_update的策略,以当
函数verify_session_user无论何时以增、删、改命令作用于表vpemp时,使用该策略。
DECLARE
    v_object_schema VARCHAR2(30) := 'public';
    v_object_name VARCHAR2(30) := 'vpemp';
    v_policy_name VARCHAR2(30) := 'secure_update';
    v_function_schema VARCHAR2(30) := 'enterprisedb';
    v_policy_function VARCHAR2(30) := 'verify_session_user';
    v_statement_types VARCHAR2(30) := 'INSERT,UPDATE,DELETE';
    v_update_check BOOLEAN := TRUE;
    v_enable BOOLEAN := TRUE;
BEGIN
    DBMS_RLS.ADD_POLICY(
        v_object_schema,
        v_object_name,
        v_policy_name,
        v_function_schema,
        v_policy_function,
        v_statement_types,
        v_update_check,
        v_enable
    );
END;
--------------
DECLARE
BEGIN
  exec dbms_rls.add_policy('public','vpemp','secure_update','enterprisedb','verify_session_user','insert,update,delete',TRUE,TRUE);
end;
--------------
3)
以用户salesmgr登录数据库,查询表vpemp:
edb=# \c edb salesmgr
edb=> SELECT * FROM vpemp;
empno | ename | job | sal | comm | deptno | authid
-------+--------+-----------+---------+---------+--------+-------------
7782 | CLARK | MANAGER | 2450.00 | | 10 |
7839 | KING | PRESIDENT | 5000.00 | | 10 |
7934 | MILLER | CLERK | 1300.00 | | 10 |
7369 | SMITH | CLERK | 800.00 | | 20 | researchmgr
7566 | JONES | MANAGER | 2975.00 | | 20 | researchmgr
7788 | SCOTT | ANALYST | 3000.00 | | 20 | researchmgr
7876 | ADAMS | CLERK | 1100.00 | | 20 | researchmgr
7902 | FORD | ANALYST | 3000.00 | | 20 | researchmgr
7499 | ALLEN | SALESMAN | 1600.00 | 300.00 | 30 | salesmgr
7521 | WARD | SALESMAN | 1250.00 | 500.00 | 30 | salesmgr
7654 | MARTIN | SALESMAN | 1250.00 | 1400.00 | 30 | salesmgr
7698 | BLAKE | MANAGER | 2850.00 | | 30 | salesmgr
7844 | TURNER | SALESMAN | 1500.00 | 0.00 | 30 | salesmgr
7900 | JAMES | CLERK | 950.00 | | 30 | salesmgr
(14 rows)

4)
更新,值更新了满足函数verify_session_user的谓词的行
edb=> UPDATE vpemp SET comm = sal * .75;
UPDATE 6

5)
验证
edb=> SELECT * FROM vpemp;
empno | ename | job | sal | comm | deptno | authid
-------+--------+-----------+---------+---------+--------+-------------
7782 | CLARK | MANAGER | 2450.00 | | 10 |
7839 | KING | PRESIDENT | 5000.00 | | 10 |
7934 | MILLER | CLERK | 1300.00 | | 10 |
7369 | SMITH | CLERK | 800.00 | | 20 | researchmgr
7566 | JONES | MANAGER | 2975.00 | | 20 | researchmgr
7788 | SCOTT | ANALYST | 3000.00 | | 20 | researchmgr
7876 | ADAMS | CLERK | 1100.00 | | 20 | researchmgr
7902 | FORD | ANALYST | 3000.00 | | 20 | researchmgr
7499 | ALLEN | SALESMAN | 1600.00 | 1200.00 | 30 | salesmgr
7521 | WARD | SALESMAN | 1250.00 | 937.50 | 30 | salesmgr
7654 | MARTIN | SALESMAN | 1250.00 | 937.50 | 30 | salesmgr
7698 | BLAKE | MANAGER | 2850.00 | 2137.50 | 30 | salesmgr
7844 | TURNER | SALESMAN | 1500.00 | 1125.00 | 30 | salesmgr
7900 | JAMES | CLERK | 950.00 | 712.50 | 30 | salesmgr
(14 rows)

6)
由于update_check参数在add_policy存储过程中设置为true,下面的insert命令会对
authid列不是salesmgr的行抛异常。
edb=> INSERT INTO vpemp VALUES (9001,'SMITH','ANALYST',3200.00,NULL,20, 'researchmgr');
ERROR: policy with check option violation
DETAIL: Policy predicate was evaluated to FALSE with the updated values
如果update_check设置为false,这个insert插入命令会成功。

例子2,列的例子
1)
设置参数sec_relevant_cols以应用策略到某些列,例如工资小于2000
CREATE OR REPLACE FUNCTION sal_lt_2000 (
    p_schema VARCHAR2,
    p_object VARCHAR2
)
    RETURN VARCHAR2
IS
BEGIN
    RETURN 'sal < 2000';
END;

2)
创建策略secure_salary使select命令在列sal,comm上时强制用该策略。
DECLARE
    v_object_schema VARCHAR2(30) := 'public';
    v_object_name VARCHAR2(30) := 'vpemp';
    v_policy_name VARCHAR2(30) := 'secure_salary';
    v_function_schema VARCHAR2(30) := 'enterprisedb';
    v_policy_function VARCHAR2(30) := 'sal_lt_2000';
    v_statement_types VARCHAR2(30) := 'SELECT';
    v_sec_relevant_cols VARCHAR2(30) := 'sal,comm';
BEGIN
    exec DBMS_RLS.ADD_POLICY(
        v_object_schema,
        v_object_name,
        v_policy_name,
        v_function_schema,
        v_policy_function,
        v_statement_types,
        sec_relevant_cols => v_sec_relevant_cols
    );
END;

3)
查询不涉及sal、comm列,所有行全返回
edb=# SELECT empno, ename, job, deptno, authid FROM vpemp;
empno | ename | job | deptno | authid
-------+--------+-----------+--------+-------------
7782 | CLARK | MANAGER | 10 |
7839 | KING | PRESIDENT | 10 |
7934 | MILLER | CLERK | 10 |
7369 | SMITH | CLERK | 20 | researchmgr
7566 | JONES | MANAGER | 20 | researchmgr
7788 | SCOTT | ANALYST | 20 | researchmgr
7876 | ADAMS | CLERK | 20 | researchmgr
7902 | FORD | ANALYST | 20 | researchmgr
7499 | ALLEN | SALESMAN | 30 | salesmgr
7521 | WARD | SALESMAN | 30 | salesmgr
7654 | MARTIN | SALESMAN | 30 | salesmgr
7698 | BLAKE | MANAGER | 30 | salesmgr
7844 | TURNER | SALESMAN | 30 | salesmgr
7900 | JAMES | CLERK | 30 | salesmgr
(14 rows)

4)
查询涉及策略的相关列,sal大于2000的被滤掉
edb=# SELECT empno, ename, job, sal, comm, deptno, authid FROM vpemp;
empno | ename | job | sal | comm | deptno | authid
-------+--------+----------+---------+---------+--------+-------------
7934 | MILLER | CLERK | 1300.00 | | 10 |
7369 | SMITH | CLERK | 800.00 | | 20 | researchmgr
7876 | ADAMS | CLERK | 1100.00 | | 20 | researchmgr
7499 | ALLEN | SALESMAN | 1600.00 | 1200.00 | 30 | salesmgr
7521 | WARD | SALESMAN | 1250.00 | 937.50 | 30 | salesmgr
7654 | MARTIN | SALESMAN | 1250.00 | 937.50 | 30 | salesmgr
7844 | TURNER | SALESMAN | 1500.00 | 1125.00 | 30 | salesmgr
7900 | JAMES | CLERK | 950.00 | 712.50 | 30 | salesmgr
(8 rows)


2
使能或使不能现有的策略在数据库对象上
以superuser用户运行
ENABLE_POLICY(object_schema VARCHAR2, object_name VARCHAR2,
policy_name VARCHAR2, enable BOOLEAN)
参数:
object_schema:策略应用到的数据库对象所在的schema名
object_name:策略应用到的数据库对象
policy_name:要删除的策略名
enable:true使能 ,false使不能。

例子1
使不能在public.vpemp上的策略secure_salary
The following example disables policy secure_update on table public.vpemp:
DECLARE
    v_object_schema VARCHAR2(30) := 'public';
    v_object_name VARCHAR2(30) := 'vpemp';
    v_policy_name VARCHAR2(30) := 'secure_salary';
    v_enable BOOLEAN := FALSE;
BEGIN
    DBMS_RLS.ENABLE_POLICY(
        v_object_schema,
        v_object_name,
        v_policy_name,
        v_enable
    );
END;

3
DROP_POLICY删除策略policy_name,但不删除相关object_schema和object_name.
以superuser运行
DROP_POLICY(object_schema VARCHAR2, object_name VARCHAR2,
policy_name VARCHAR2)
参数:
object_schema:策略应用到的数据库对象所在的schema名
object_name:策略应用到的数据库对象
policy_name:要删除的策略名

例子1:
删除应用到对象public.vpemp上的策略secure_update
DECLARE
    v_object_schema VARCHAR2(30) := 'public';
    v_object_name VARCHAR2(30) := 'vpemp';
    v_policy_name VARCHAR2(30) := 'secure_salary';
BEGIN
    DBMS_RLS.DROP_POLICY(
        v_object_schema,
        v_object_name,
        v_policy_name
    );
END;


你可能感兴趣的:(PostgreSQL,PostgreSQL,PostgreSQL,虚拟私有库,数据库安全控制)