什么是VPD
所谓虚拟专用数据库(VPD)指的是,通过在数据库里进行配置,从而让不同的用户只能查看某个表里的部分数据。VPD分为以下两个级别:
行级别:在该级别下,可以控制某些用户只能查看到某些行数据。例如对于销售数据表来说,每一名销售人员仅仅能检索自己的销售数据,而不能检索其他用户的销售数据。
列级别:在该级别下,可以控制某些用户不能检索某些列的数据。例如HR用户下的EMPLOYEES表,包含salary这一个列,表示工资。由于该列不允许任何用户都可以看到,所以可以在这个列上使用一些设置,这样其他一些用户查看这一列信息时,其数值均为NULL。
利用Oracle的细粒度访问控制,可用一种非常复杂的方式调整安全策略,可将细粒度访问控制用于以下目的:
l 通过SELECT、INSERT、UPDATE、DELETE语句实施行访问控制
l 建立一个安全策略,它基于某个列的特定值控制访问
l 创建与查询执行中动态更改策略相同的方式使用的策略
l 创建一组安全策略,成为策略组
基于行的VPD
基于行的VPD也叫做Fine-Grained Access Control,即FGAC。FGAC通过定义规则来实现,规则的集合叫做FGAC政策(Policy)。如果对某个表设置了FGAC,则当用户对该表发出查询或者DML语句时,Oracle都会根据定义的FGAC政策,而自动改写这些SQL语句。其改写方式为自动在SQL语句后面添加where条件。
使用FGAC策略来限定返回记录的方式具有很多优点。例如:不需要改写应用程序,对用户完全透明,集中设置,便于管理等。
在使用FGAC时,会涉及到应用程序上下文(Application
Context)的概念,使用应用程序上下文可以简化FGAC管理的实现。应用程序上下文是一个数据库对象,可以把它理解为数据库里的每一个session的全局环境变量。一旦用户登录到数据库,从而创建出session以后,应用程序上下文就在整个session的生命周期里可用。在应用程序上下文里可以定义多个属性,并为这些属性设置具体的值。而用户不能直接修改属性的值,只能通过程序包来修改属性值。应用程序上下文总是有用户SYS拥有的。
例如:对于用户HR的EMPLOYEES表而言,我们可以创建一个应用程序上下文,当用户登录时,将该用户的ID号作为一个属性值放入该应用程序上下文中。然后再定义FGAC政策的时候,将该用户ID号取出,并作为限定条件短语返回给Oracle,从而实现FGAC。
在Oracle数据库中,已经为每一个session都预先建立了一个应用程序上下文:userenv。
查看方法,通过下面的查询可以看到:
select
sys_context('userenv','ip_address') as
"IP",sys_context('userenv','db_name') as "DB" from dual
SQL>
select sys_context('userenv','terminal') as "application ctx" from
dual;
application
ctx
--------------------------------------------------------------------------------
646C3C9C9D8E4DA
下表列出了USERENV命名空间中常用的预定义属性
属性
说明
Instance
实例ID
Entryid
审计条目标识
Current_user
启动会话的用户名称
Session_user
数据库用户名,当前用户通过它进行认证
Proxy_user
为会话用户打开一个会话的中间层次名称
Db_name
数据库名称
Host
数据库在其上运行的主机名
Os_user
操作系统帐号名
Terminal
用来访问数据库的客户机终端
Ip_address
客户机的IP地址
External_name
数据库用户的外部名称
在用户登录时,识别用户的类型和获得关键的用户属性非常重要,以后可将这些信息用于附加到数据库对象的安全策略中,将内建的USERENV命名空间用来获得这些信息是很理想的。
USERENV命名空间仅是可用的应用环境命名空间之一。你还必须创建自己的应用环境,以便能够规定在设置自己的安全策略时需要使用哪些属性。为了定义自己的应用环境,还需要做以下工作:
(1)创建一个利用各种函数的帮助设置环境的PLSQL程序包
(2)创建一个使用所创建的程序包的应用环境。
l 创建设置环境的程序包
如果要为用户hr设置应用环境,需要创建一个PLSQL程序包。
下面的代码说明如何创建一个名为HR_CONTEXT的设置应用环境的简单程序包。此程序包包含一个过程,它选择employee_id列的值到empnum变量。因为这条SELECT语句基于一条WHERE子句根据SESSION_USER属性确定雇员的last_namae,所以此employee_id将是数据库用来认证当前用户的用户名的employee_id。:
SQL> conn hr/hr;
已连接。
create or replace
package hr_context is
-- Author : BURKE
-- Created : 2008-4-26 17:02:34
-- Purpose :
procedure select_emp_no;
end hr_context;
create or replace package
body hr_context is
procedure select_emp_no is
empnum number;
begin
select employee_id into empnum from employees where
upper(last_name)=sys_context('USERENV','SESSION_USER');
dbms_session.set_context('employee_info','emp_num',empnum);
end select_emp_no;
end hr_context;
l 创建应用环境
一旦创建了帮助设置应用环境的程序包(HR_CONTEXT),则可以如下创建应用环境本身。请注意,hr用户使用前一节创建的程序包创建employee_info应用环境。
connect system/sysadmin@burke as sysdba;
grant create any context
to hr;
conn hr/hr;
CREATE
CONTEXT employee_info USING hr.context;
SQL> CREATE
CONTEXT employee_info USING hr.context;
上下文已创建。
可以用两种方法设置一个用户的应用环境。第一种方法是通过它自身实现一个应用环境,没有FGA的访问控制。为完成此工作,创建一个用户登录时的事件触发器,这样用户在登录数据库时将调用属于HR_CONTEXT程序包的SELECT_EMP_ON过程。
Conn
system/sysadmin@burke as sysdba;
create or replace
trigger hr.security_context
after logon on
database
declare
-- local variables
here
begin
hr_context.select_emp_no;
end security_context;
通过登录触发器使用了程序包的过程,它用来获取用户的employee_id并存储到emp_num变量。
第二种设置或引用应用环境的方法是利用VPD的完成成分,它使用一个策略函数来实现FGA的访问控制。
实现FGA的访问控制
在一些场合下,我们可能想限制某些用户对应用程序数据的访问。当然,可以通过视图来达到这一目的,但管理视图也会带来一些问题。如维护和审计等问题。
FGA允许限制Oracle用户,使得他们只能使用你让他们访问和修改的数据。使用策略函数(policy
function)有助于FGAC,策略函数附加到想要保护的表或者视图上。它使用动态可修改的语句将用户约束或者限制在表、视图或同义词的某些部分中。在分析用户的SQL语句时,FGAC让Oracle自动估计策略函数(一个表可以附加不止一个策略)。Oracle将在动态修改查询(如果有必要)后执行它。
注解:FGAC允许实现细颗粒度数据安全,使用这个特性,可实施行级安全策略。
FGAC涉及以下步骤:
(1)创建给用户的DML语句动态增加一个谓词的策略函数。谓词是基于某个操作符的WHERE子句。
Cust_no=(
Select custno from orders
whre
custname=SYS_CONTEXT(‘USERENV’,’SESSION_USER’)
)
实现安全策略的程序包将动态的附加一个词到ORDERS表上的所有SELECT语句中,只返回与该用户的客户名(cust_no)有关的那些订单。
(2)用户输入下面这样一条语句:
SELECT * FROM
ORDERS;
(3) Oracle将使用你创建的策略函数动态修改用户的语句。例如步骤2中的语句将被步骤1中的策略函数修改如下:
select * from orders where custno=(
select custno from customers
where custname=SYS_context('USERENV','SESSION_USER')
)
(4)
Oracle使用SYS_context('USERENV','SESSION_USER')返回的用户名对原查询进行修改,从而限制从ORDERS表返回的数据仅为该用户的数据。
创建hr_security程序包
create or replace
package HR_SECURITY is
-- Author : BURKE
-- Created : 2008-4-26 18:23:47
-- Purpose :
function empnum_sec(a1 varchar2,a2 varchar2)return
varchar2;
end HR_SECURITY;
create or replace package
body HR_SECURITY is
function empnum_sec(a1 varchar2,a2 varchar2)return
varchar2
is
d_predicate varchar2(2000);
begin
d_predicate:='employee_id=SYS_CONTEXT("EMPLOYEE_INFO","EMP_NUM")';
RETURN d_predicate;
end;
end HR_SECURITY;
上面的程序包hr_security将使用employee_info环境获取emp_num变量。
HR_SECURITY包中的d_predicate谓词指出,这种变换应该用于其employee_id与从employee_info环境取得的emp_num变量想匹配的任何雇员的查询。例如:如果用户salapati发布下面的命令:
select * from employees;
它将被我们的谓词修改为:
select * from employees
where employee_id=SYS_CONTEXT("EMPLOYEE_INFO","EMP_NUM");
创建安全策略
前一小节创建的程序包附加了一个动态谓词到某些SQL语句,这些SQL语句是employee_id与用employee_info应用环境导出的emp_num想匹配的雇员使用的。但是,我们仍然没有给EMPLOYEES表附加安全策略。也就是说,我们现在必须准确地指出HR_SECURITY程序包将应用于何种SQL语句,什么样的表。
在Oracle10gR2版本中,可以使用dbms_pls.add_policy过程为POLICY_TYPE参数指定以下5种类型的安全策略:
l 动态
l 静态
l 共享静态
l 环境敏感
l 共享环境敏感
(----.静态策略类型
对于静态策略类型,我们可以将参数POLICY_TYPE的值指定为DBMS_RLS.STATIC。在静态的策略类
型中,谓词在不同运行环境下都是相同的。静态策略函数运行一次,并且被缓存到SGA,这样静态策略
函数就非常快。访问相同对象的语句不需要重复运行策略函数,当然根据SYS_CONTEXT和SYSDATE
这样的属性不同,每次运行也可以产生不同的结果。 ----.共享-静态策略类型
当一个函数用于多个策略中的时候,我们称之为共享策略。共享策略可以在多个对象的业务处理策略相
同的时候,不必为每个对象都创建一个策略函数。我们应该将POLICY_TYPE设置为
DBMS_RLS.SHARED_STATIC。它的处理方式与STATIC相同。
----环境敏感策略
我们需要将参数POLICY_TYPE的值指定为DBMS_RLS.CONTEXT_SENSITIVE。如果数据库发现自上
次使用以后,运行的上下文环境发生了变化,那么就会对上下文敏感策略函数重新求值。
----共享-环境敏感策略类型
与环境敏感策略类似,只不过函数是可以共享的。我们需要将POLICY_TYPE指定为
DBMS_RLS.SHARED_CONTEXT_SENSTIVE。)
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema=>'hr',
object_name=>'employees',
policy_name=>'manager_policy',
function_schema=>'hr',
policy_function=>'hr_security.empnum_sec',
statement_types=>'select'
);
END;
可以通过下面的查询验证新策略是否确实已经成功创建:
select object_name,policy_name,sel,ins,upd,del,enable from all_policies;
SQL>
select object_name,policy_name,sel,ins,upd,del,enable
2 from all_policies;
OBJECT_NAME POLICY_NAME SEL INS UPD DEL ENA
------------------------------
------------------------------ --- --- --- --- ---
EMPLOYEES MANAGER_POLICY YES NO NO NO YES
删除策略
begin
dbms_rls.drop_policy(
object_schema=>'hr',
object_name=>'employees',
policy_name=>'manager_policy'
);
end;
查询结果表明针对EMPLOYEES表的所有select语句现在由MANAGER_POLICY安全策略控制。
为使公众可以访问安全策略函数,以便所有访问数据库的用户能够使用它,可进行下面的授权
SQL>
grant execute on hr_security to public;
授权成功。
基于列的VPD
我们已经看到如何在访问表时实施行级安全策略。Oracle还允许使用列级VPD,在一个查询引用某个列或某些列时实现列级安全。列级VPD可以应用于表和视图。
创建列级安全策略与创建普通的安全策略几乎是相同的,只需在DBMS_RLS.ADD_POLICY过程中添加SEC_RELEVANT_COLS参数指出与安全相关的列即可。下面示例指出如何使用DMBS_RLS.ADD_POLICY过程创建一个列级安全策略。
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema=>'hr',
object_name=>'employees',
policy_name=>'manager_policy',
function_schema=>'hr',
policy_function=>'hr_security.empnum_sec',
statement_types=>'select,insert',
sec_relevant_cols=>'salary'
);
END;
上面的例子中创建的列级安全策略仅仅在访问EMPLOYEE表的SALARY列时起作用。假如某个用户随后发出下面的查询:
Select
fname,lname,salary from employees;
列级VPD策略在查询中将通过列的安全策略转换SQL语句为:
select fname,lname,salary from employees
where salary='my_salary';