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;