4.3 屏蔽敏感的列
背景 把您自己想象成为一名业余数据库黑客。 这里的数据库包含医疗记录,您正在寻找的信息是诊断代码。 您将寻找哪些列? 可能是称为诊断、疾病或者类似的列。
正如您可以看到的,带有明显名字的敏感列是至关重要的安全问题。 策略 如果对手以前不了解您数据库的内容,并且这些列的名字不直观,他们将不会去解读列的含义。 这个策略称为“无名安全”。 即使故意闯进数据库的经验丰富的黑客在做其它事情之前仍将需要追捕到列的名称。 因为他们的时间可能有限——几乎一直都是这样——他们通常会转到下一个机会上。 当然,使用无名列也加大了开发的难度。
进行折衷还有其它的办法,然而: 通过列屏蔽,您可以隐藏列的内容,只有合法用户才可以看到列的内容。
列屏蔽的方法有两种: 使用视图和使用 VPD。
使用视图。 这种方法适用于 Oracle 任何版本,但是如果您的数据库版本是 Oracle9i 或者更早,就只能选择这种方法。
假设您的表和以下类似: SQL> desc patient_diagnosis
Name Null?类型
------------------- ------ -------------
PATIENT_ID NUMBER
DIAGNOSIS_ID NUMBER
DIAGNOSIS_CODE VARCHAR2(2)
DOCTOR_ID NUMBER(2)
BILLING_CODE NUMBER(10)
行类和以下类似: SQL> select * from patient_diagnosis;
PATIENT_ID DIAGNOSIS_ID DI DOCTOR_ID BILLING_CODE
---------- ------------ -- ---------- ------------
1 1 01 1 1003
1 2 02 1 1003
1 3 11 1 1003
2 1 11 1 1005
2 2 41 2 1005
在这里,您想屏蔽列 DIAGNOSIS_CODE 的值。 create view vw_patient_disgnosis
as
select
patient_id,
diagnosis_id,
doctor_id,
billing_code
from patient_diagnosis
/
然后,您可以给视图 VW_PATIENT_DIAGNOSIS 创建同义词 PATIENT_DIAGNOSIS,授予视图(而不是表)选择。 该视图屏蔽列 DIAGNOSIS_CODE。
这是一个相当简单的解决方案,因此您不要将所有用户的列屏蔽掉,您可能想创建基于角色的匿名——如果用户是一名经理,请显示被保护的列;否则的话不要显示。其方法是:传递应用程序环境或者全球变量以指定用户的角色。 如果应用程序环境属性是 IS_MANAGER,您可以使用: create or replace view vw_patient_disgnosis
as
select
patient_id,
diagnosis_id,
decode(
sys_context('USER_ROLE_CTX','IS_MANAGER'),
'Y', DIAGNOSIS_CODE, null
) diagnosis_code,
doctor_id,
billing_code
from patient_diagnosis;
这是一个更加灵活的视图,该视图可以授权给所有的用户,该视图的内容会随着用户角色的不同而不同。
使用 VPD。 Oracle 数据库 10g 中引入的新特性使 VPD 更加有用: 创建视图是没有必要的。 相反,VPD 策略可能抑制显示。 这里,VPD 策略函数和以下类似: 1 create or replace function pd_pol
2 (
3 p_schema in varchar2,
4 p_obj in varchar2
5 )
6 return varchar2
7 is
8 l_ret varchar2(2000);
9 begin
10 if (p_schema = USER) then
11 l_ret := NULL;
12 else
13 l_ret := '1=2';
14 end if;
15 return l_ret;
16 end;
现在构建策略函数: 1 begin
2 dbms_rls.add_policy (
3 object_schema => 'ARUP',
4 object_name => 'PATIENT_DIAGNOSIS',
5 policy_name => 'PD_POL',
6 policy_function => 'PD_POL',
7 statement_types => 'SELECT',
8 update_check => TRUE,
9 sec_relevant_cols => 'DIAGNOSIS_CODE',
10 sec_relevant_cols_opt => dbms_rls.all_rows
11 );
12 end;
注意行号 9 和 10。在第 9 行中,我们提及将列 DIAGNOSIS_CODE 作为敏感列。 在第 10 行中,我们指出如果选择了该列,VPD 显示所有列;但是该列值却显示为 NULL。 这就有效地屏蔽了该列。 在策略函数中,请注意如果表的所有者进行选择,应用的谓词为 NULL。因此,VPD 限制就不被应用,列也不显示。
请记住,没有“更换”策略的方法。 如果旧策略还存在,您必须首先抛弃旧策略。 begin
dbms_rls.drop_policy (
object_schema => 'ARUP',
object_name => 'PATIENT_DIAGNOSIS',
policy_name => 'PD_POL'
);
end;
现在,您可以进行测试。 SQL> conn arup/arup
Connected.
SQL> select * from patient_diagnosis;
PATIENT_ID DIAGNOSIS_ID DI DOCTOR_ID BILLING_CODE
---------- ------------ -- ---------- ------------
1 1 01 1 1003
1 2 02 1 1003
1 3 11 1 1003
2 1 11 1 1005
2 2 41 2 1005
注意:DIAGNOSIS_CODE 列值显示出来了,因为 ARUP 是表的所有者,ARUP 应该看得到这些值。 现在,以对该表有选择权限的另一名用户身份进行连接,执行相同的查询。 SQL> set null ?
SQL> conn ananda/ananda
SQL> select * from arup.patient_diagnosis;
PATIENT_ID DIAGNOSIS_ID D DOCTOR_ID BILLING_CODE
---------- ------------ - ---------- ------------
1 1 ? 1 1003
1 2 ? 1 1003
1 3 ? 1 1003
2 1 ? 1 1005
2 2 ? 2 1005
注意列 DIAGNOSIS_CODE 是如何显示所有空值的。
除了不需要在表上创建视图,不需要创建同义词指向该视图,不需要维护额外的授权之外,该方法是更加优秀的。 如果您需要拥有不同的策略把该值显示给不同的人,您可以很容易地修改策略函数(第 11 行)添加更多的检查。 例如,您的策略可以是:所有用户都可以看到敏感性不强的诊断代码(例如普通感冒)。 因此,您的策略函数和下面的类似,假设一般性感冒的 DIAGNOSIS_CODE 为“01”。 1 create or replace function pd_pol
2 (
3 p_schema in varchar2,
4 p_obj in varchar2
5 )
6 return varchar2
7 is
8 l_ret varchar2(2000);
9 begin
10 if (p_schema = USER) then
11 l_ret := NULL;
12 else
13 l_ret := 'diagnosis_code=''01''';
14 end if;
15 return l_ret;
16* end;
注意:仅当诊断代码为“01”而不是其它时,我们在第 13 行添加要显示的额外谓词。 现在进行测试。 SQL> conn arup/arup
Connected.
SQL> select * from arup.patient_diagnosis;
PATIENT_ID DIAGNOSIS_ID DI DOCTOR_ID BILLING_CODE
---------- ------------ -- ---------- ------------
1 1 01 1 1003
1 2 02 1 1003
1 3 11 1 1003
2 1 11 1 1005
2 2 41 2 1005
SQL> conn ananda/ananda
Connected.
SQL> select * from arup.patient_diagnosis;
PATIENT_ID DIAGNOSIS_ID DI DOCTOR_ID BILLING_CODE
---------- ------------ -- ---------- ------------
1 1 01 1 1003
1 2 ? 1 1003
1 3 ? 1 1003
2 1 ? 1 1005
2 2 ? 2 1005
注意:诊断代码“01”代表病人 id 1 和诊断 id 1,这是唯一允许的诊断代码;因此显示得很清晰。 其它的显示已经显示为孔,并且得到有效地屏蔽。
提示 如果您想屏蔽敏感列,程序也没有提及到它们,就没有提示。
如果您使用视图方法(这里敏感列只是从该视图中删除),则将会引发一个问题,这里程序使用像 SELECT * FROM TABLE ... 一样的结构。 因为已经没有明确对列进行命名,缺少一列将影响到程序执行。 但是,这不是一个关于修改视图方法(在这里列仍然存在但是为空)的问题。
有一个您应该意识到的很重要的提示。 假如您有一个称为“CONTRACT_AMOUNT”的列;如果数值小于某个特定值(比如 500),该列就显示。如果数值大于 500,该列就显示为空。 该表有三行,这三行所在列的数值分别为 300, 300, 600。 在列屏蔽之前,如果用户提交以下查询 select avg (contract_amount) from contracts;
他将得到 400 (就是 300,300 和 600 的平均值)。 在列屏蔽之后,数值为 $600 的列的值将变为空,因此用户将看到的值分别为 300,300 和空。 现在,输入同样的查询,将会显示 200 (300,300 和空)。 请注意一个重要的区别: 所显示的值为 200;而不是 400。
请意识到列屏蔽可能引入的这种重要区别。
操作计划
- 在所有敏感的表上列出所有敏感的列。
- 确定将要屏蔽的敏感的列。
- 确定权限模式。
- 如果您使用的是 Oracle 数据库 10g 第 1 版或更高版本,请选择 VPD 方法。
另外, 选择基于视图的方法。
- 如果您选择 VPD 方法:
- 给各个表构建策略函数。
- 给各个表构建策略。 这将帮助您在特定的表上对屏蔽进行控制。
- 如果您选择基于视图的方法:
- 在各个表(表的名字通常为 VW_<table_name>)上创建视图,
- 创建指向该视图的同义词(同义词的名称和表的名称相同)。
- 在表上撤消给用户授予的权限。
- 再次给视图授予权限。
- 重新编译所有依赖的无效对象。
(编辑:aniston)
|