使用VPD限制数据访问
一 概述
虚拟专用数据库 (VPD) 在确保物理数据分离的情况下按用户或客户来控制数据访问。对于互联网访问,虚拟专用数据库可以确保在线银行的客户只能看到他们自己的帐户。Web 托管公司可以在同一 Oracle 数据库中维护多个公司的数据,但只允许每个公司查看其自身数据。
在企业内部,虚拟数据库可在应用程序部署方面降低拥有成本。可以在数据库服务器一次实现安全性,而不用在访问数据的每个应用程序中分别实现安全性。因为是在数据库中实施安全性,所以不管用户访问数据的方式如何,安全性较以前更高。访问即席查询工具或新报表生成程序的用户不再能绕过安全环节。虚拟专用数据库是一项重要技术,使企业能够构建托管的、基于 Web 的应用程序。实际上,许多 Oracle 应用程序本身使用 VPD 实施数据分隔,包括 Oracle SalesOnline.com
和 Oracle Portal 等程序。
二 工作原理
将一个或多个安全策略与表或视图关联后,就可以实现虚拟专用数据库。对带安全策略的表进行直接或间接访问时,数据库将调用一个实施该策略的函数。策略函数返回一个访问条件(WHERE 子句),即谓词。应用程序将它附加到用户的 SQL 语句,从而动态修改用户的数据访问权限。
你可以通过编写一个存储过程将 SQL 谓词附加到每个 SQL 语句(用于控制该语句的行级别访问权限)来实施 VPD。例如,如果 John Doe(他属于 Department 10)输入 SELECT * FROM emp 语句,则可以使用 VPD 添加 WHERE DEPT = 10 子句。这样,您便可以通过对查询进行修改来限制访问某些行的数据。
虚拟专用数据库确保无论用户以何种方式访问数据(通过应用程序、报表编写工具或 SQL*Plus) ,都将强制实施同一强大的访问权限控制策略。这样,使用 VPD ,银行便可以确保客户只看到他们自己的帐户,电信公司可以安全地隔离客户记录,人力资源应用程序可以支持复杂的员工记录数据访问原则。
DBMS_RLS Procedures
Procedure | Description |
---|---|
For Handling Individual Policies |
|
Adds a policy to a table, view, or synonym |
|
Enables (or disables) a policy you previously added to a table, view, or synonym |
|
Invalidates cursors associated with nonstatic policies |
|
To drop a policy from a table, view, or synonym |
|
For Handling Grouped Policies |
|
Creates a policy group |
|
Drops a policy group |
|
Adds a policy to the specified policy group |
|
Enables a policy within a group |
|
Parses again the SQL statements associated with a refreshed policy |
|
Disables a policy within a group |
|
Drops a policy that is a member of the specified group |
|
For Handling Application Contexts |
|
Adds the context for the active application |
|
Drops the context for the application |
三 创建静态的VPD
1 **OE用户
SELECT USERNAME, ACCOUNT_STATUS FROM DBA_USERS WHERE USERNAME = 'OE';
ALTER USER OE ACCOUNT UNLOCK IDENTIFIED BY test;
2、创建策略,只查询表OE.ORDERS中SALES_REP_ID = 159用户的记录。
(1)创建策略前
SELECT * FROM OE.ORDERS;
SELECT * FROM OE.ORDERS WHERE SALES_REP_ID = 159;
(2)创建策略函数auth_orders
1
2
3
4
5
6
7
8
9
10
11
|
CREATE OR REPLACE FUNCTION auth_orders(
schema_var IN VARCHAR2,
table_var IN VARCHAR2
)
RETURN VARCHAR2
IS
return_val VARCHAR2 (400);
BEGIN
return_val := 'SALES_REP_ID = 159' ;
RETURN return_val;
END auth_orders;
|
(3)创建VPD策略
1
2
3
4
5
6
7
8
9
10
|
BEGIN DBMS_RLS.ADD_POLICY (
object_schema => 'oe' ,
object_name => 'orders' ,
policy_name => 'orders_policy' ,
function_schema => 'sys' ,
policy_function => 'auth_orders' ,
statement_types => 'select, insert, update, delete'
);
END ;
|
备注:object_schema表示数据表(视图)object_name所在的方案名称,object_name表示数据表(视图)的名称,policy_name表示给策略取得名称,方便对策略进行管理,function_schema表示返回Where子句的函数所在方案的名称,policy_function表示返回Where子句的函数名称,statement_types表示要使用该策略的DML类型,如:select, insert, update, delete等。
3 测试
使用OE登录,输入如下语句:
SELECT Count(*) FROM ORDERS;
返回值为7。
切换到sys用户登录。输入如下语句:
SELECT Count(*) FROM OE.ORDERS;
返回值为105,说明VPD策略起作用。
4 删除策略函数和策略
以sys用户登录,输入如下命令:
DROP FUNCTION auth_orders;
EXEC DBMS_RLS.DROP_POLICY('OE','ORDERS','ORDERS_POLICY');
四 创建基于上下文相关的VPD
1 创建VPD策略管理员用户sysadmin_vpd并授权
1
2
3
|
GRANT CREATE SESSION, CREATE ANY CONTEXT, CREATE PROCEDURE , CREATE TRIGGER , ADMINISTER DATABASE TRIGGER TO sysadmin_vpd IDENTIFIED BY 密码;
GRANT EXECUTE ON DBMS_SESSION TO sysadmin_vpd;
GRANT EXECUTE ON DBMS_RLS TO sysadmin_vpd;
|
2 **scott用户
1
2
|
SELECT USERNAME, ACCOUNT_STATUS FROM DBA_USERS WHERE USERNAME = 'SCOTT' ;
ALTER USER SCOTT ACCOUNT UNLOCK IDENTIFIED BY 密码;
|
3 创建(上下文用户)数据库登录用户数据表scott.customes,登录用户名是cust_email字段的值
1
2
3
4
5
6
7
|
CREATE TABLE scott.customers ( cust_no NUMBER( 4 ),
cust_email VARCHAR2( 20 ),
cust_name VARCHAR2( 20 ));
INSERT INTO scott.customers VALUES ( 1234 , 'T*E' , 'Thadeus *e' );
INSERT INTO scott.customers VALUES ( 5678 , 'OWOODS' , 'Oberon Woods' );
|
4 创建测试用户
1
2
|
GRANT CREATE SESSION TO t*e IDENTIFIED BY 密码;
GRANT CREATE SESSION TO owoods IDENTIFIED BY 密码;
|
5 将scott.customers表select权限授权给sysadmin_vpd(该表不需要授权给数据库登录用户t*e
和owoods
,只是存储过程需要使用该表)
1
|
GRANT SELECT ON scott.customers TO sysadmin_vpd;
|
6 创建样例测试数据表scott.orders_tab,该表中的cust_no
字段和scott.customers
.cust_no
一一对用
1
2
3
4
5
6
|
CREATE TABLE scott.orders_tab (
cust_no NUMBER(4),
order_no NUMBER(4));
INSERT INTO scott.orders_tab VALUES (1234, 9876);
INSERT INTO scott.orders_tab VALUES (5678, 5432);
INSERT INTO scott.orders_tab VALUES (5678, 4592);
|
7 将该表授权给数据库登录用户t*e和owoods
1
2
|
GRANT SELECT ON scott.orders_tab TO t*e;
GRANT SELECT ON scott.orders_tab TO owoods;
|
8 创建基于会话的应用程序上下文(以sysadmin_vpd用户登录)
以使用create context命令创建应用程序上下文,并且使用程序包DBMS_RLS管理VPD策略。可以像其他任何函数一样创建用于返回谓词以实施策略的函数,但这种函数具有两个必需的参数,并且返回一个VARCHAR2。
使用create context命令,可以创建应用程序定义的属性的名称,这些属性用于实施安全策略。此外,还可以定义函数和过程的程序包名称,这些函数和过程用于设置用户会话的安全上下文。
以sysadmin_vpd用户登录,创建如下内容:
1
|
CREATE OR REPLACE CONTEXT orders_ctx USING orders_ctx_pkg;
|
1
2
3
|
CREATE OR REPLACE PACKAGE orders_ctx_pkg IS
PROCEDURE set_custnum;
END ;
|
1
2
3
4
5
6
7
8
9
10
11
12
|
CREATE OR REPLACE PACKAGE BODY orders_ctx_pkg IS
PROCEDURE set_custnum
AS
custnum NUMBER;
BEGIN
SELECT cust_no INTO custnum FROM SCOTT.CUSTOMERS
WHERE cust_email = SYS_CONTEXT( 'USERENV' , 'SESSION_USER' );
DBMS_SESSION.SET_CONTEXT( 'orders_ctx' , 'cust_no' , custnum);
EXCEPTION
WHEN NO_DATA_FOUND THEN NULL ;
END set_custnum;
END ;
|
在该示例中,上下文名称是orders_ctx,用于在会话期间为用户建立特征或属性的程序包称为orders_ctx_pkg。在登录触发器中调用过程set_custnum。因为上下文orders_ctx只绑定到orders_ctx_pkg,因此没有其他的过程可以改变会话属性。这可以确保在连接到数据库后用户或任何其他进程都不可以改变安全的应用程序上下文。
在用于实现应用程序上下文的典型程序包中,使用内置的上下文USERENV来检索有关用户会话自身的信息。下表是USERENV上下文中一些更为常见的参数。
参数 |
返回值 |
CURRENT_SCHEMA |
会话的默认模式 |
DB_NAME |
在初始参数DB_NAME中指定的数据库名称 |
HOST |
用户连接的主机名称 |
IP_ADDRESS |
用户连接的IP地址 |
OS_USER |
初始化数据库会话的操作系统账户 |
SESSION_USER |
经过验证的数据库用户名 |
DBMS_SESSION.SET_CONTEXT (
namespace VARCHAR2,
attribute VARCHAR2,
value VARCHAR2,
username VARCHAR2,
client_id VARCHAR2 );
参数的含义:
namespace 这个上下文的名字
attribute 属性值,即为key值
value 值
username 用户名 默认null,可选值
client_id 指定的clientid 默认null,可选值
备注:在该实例中给
的值。DBMS_SESSION.SET_CONTEXT(
'orders_ctx'
,
'cust_no'
, custnum)
含义是:给上下文
中的SYS_CONTEXT增加一个属性,属性名为orders_ctx
cust_no,属性值为
便利custnum的值。以后再上下文
中就可以通过orders_ctx
SYS_CONTEXT('
'orders_ctx'
', '
'cust_no'
')
来获得属性cust_no
9 创建登录触发器(以sysadmin_vpd用户登录
)
以sysadmin_vpd用户登录,创建如下触发器:
1
2
3
4
|
CREATE TRIGGER set_custno_ctx_trig AFTER LOGON ON DATABASE
BEGIN sysadmin_vpd.orders_ctx_pkg.set_custnum;
END ;
|
只有在应用程序上下文中用户登录才能触发该触发器。可以使用如下命令查看:
SELECT SYS_CONTEXT('orders_ctx', 'cust_no') custnum FROM DUAL;
10 创建策略函数get_user_orders,只显示登录用户的订单(以sysadmin_vpd用户登录
)
1
2
3
4
5
6
7
8
9
10
|
CREATE OR REPLACE FUNCTION get_user_orders(
schema_p IN VARCHAR2,
table_p IN VARCHAR2)
RETURN VARCHAR2
AS
orders_pred VARCHAR2 (400);
BEGIN
orders_pred := 'cust_no = SYS_CONTEXT(' 'orders_ctx' ', ' 'cust_no' ')' ;
RETURN orders_pred;
END ;
|
11 创建策略(以sysadmin_vpd用户登录
)
1
2
3
4
5
6
7
8
9
|
BEGIN DBMS_RLS.ADD_POLICY (
object_schema => 'scott' ,
object_name => 'orders_tab' ,
policy_name => 'orders_policy' ,
function_schema => 'sysadmin_vpd' ,
policy_function => 'get_user_orders' ,
statement_types => 'select' );
END ;
|
12 测试
以t*e用户登录,结果:
以owoods以后登录,结果:
以scott用户登录,查询到数据为0,只有以sys用户登录,可以查询到所有数据。
查看策略应用情况:
1
|
select object_owner,object_name,policy,predicate from V$VPD_POLICY;
|
若需要scott用户能查询到所记录,修改策略函数为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
CREATE OR REPLACE FUNCTION get_user_orders(
schema_p IN VARCHAR2,
table_p IN VARCHAR2)
RETURN VARCHAR2
AS
v_email VARCHAR2(20);
BEGIN
v_email := sys_context( 'USERENV' , 'SESSION_USER' );
if v_email = 'SCOTT' Then
retun null ;
else
return 'cust_no = SYS_CONTEXT(' 'orders_ctx' ', ' 'cust_no' ')' ;
end if;
END ;
|
备注:用户名“SCOTT”必须大写,否则无效。
13 删除创建的数据,回复原样
1
2
3
4
5
6
|
BEGIN DBMS_RLS.DROP_POLICY (
object_schema => 'scott' ,
object_name => 'orders_tab' ,
policy_name => 'orders_policy' );
END ;
|
1
2
3
4
5
6
|
DROP TABLE scott.orders_tab;
DROP TABLE scott.customers;
DROP CONTEXT orders_ctx;
DROP USER sysadmin_vpd CASCADE ;
DROP USER t*e cascade ;
DROP USER owoods cascade ;
|
本文转自stock0991 51CTO博客,原文链接:http://blog.51cto.com/qing0991/1364386,如需转载请自行联系原作者