实现对 DB2 UDB 的低层访问控制(2)

http://www.itjxue.com  2015-08-21 22:27  来源:未知  点击次数: 

  实现写访问策略

  与读访问一样,对授予主题表上写权限(即 INSERT、UPDATE 或 DELETE 权限)的控制是最基本的防御。如果用户没有权限,就不能执行操作。当然,如果一个表需要修改,那么假设您可以避免授权该表的写访问是不现实的。如果您必须授予用户对表的写权限,那最好尽可能最小化授权范围。例如,授予 UPDATE 权限时,如果只需更新所有列的一个子集,那么就只授予所必需的列上的权限。又与读权限一样,您也可以选择使用静态 SQL 或动态 SQL 授权模式,DB2 提供这些模式来控制谁在何种上下文中拥有何种权限。

 

  无论何时,只要条件允许,我们同样推荐以视图作为首要的写访问点。通过向用户授予视图上的写权限,您可以避免授予主题表上的直接权限。无论是从权限还是从表的内容和结构出发,控制对主题表的直接访问时,都允许从用户角度来维护一个一致的外部访问点。视图定义中 WITH CHECK OPTION 从句的使用,确保了任何企图通过视图进行的写访问都要满足视图自身的条件。视图定义中的 WITH CHECK OPTION 确保企图在视图上进行的写访问与通过视图所读取的行在逻辑上要保持一致。也就是说,正在修改的行对于通过此视图定义提出修改请求的用户必须是可见的。该条件适用于所有的 INSERT、UPDATE 和 DELETE 操作。当通过读和写访问策略为某个用户定义的行集合相同时,该条件就十分强大。因为那个用户仅通过视图定义以及视图上的授权就可以执行所有访问,而无须附加任何实施机制。在前面讨论如何实现读访问控制时,我们给出了视图定义。下面的例子展示了如何修改此定义,使其包括 WITH CHECK OPTION 从句。

— 创建这样的视图,以允许雇员访问 employee 表中自己的行和某些列
create view bigco.employee_access
  (Employee_ID, User_ID, Employee_Information, Department_ID) as
(select employee_id, employee_userid, employee_info, department_id
from employee
where employee_userid = USER);
with check option;
— 创建这样的视图,以允许 HR 部门和管理部门访问所有的行
create view bigco.mgmt_access
  (Employee_ID, User_ID, Employee_Information, Department_ID, Employee_Evaluation) as
(select employee_id, employee_userid, employee_info, department_id,
    case when (department_id = ‘d666’)
      then case (select department_id
          from bigco.employee as x
where x.employee_userid = USER)
        when ‘d666’ then employee_evaluation
else ‘N/A’
      end      
        else employee_evaluation
    end        
  from bigco.employee);
with check option;

  有了这些新修改的视图定义,我们就可以授予所需的权限,以实现前面定义的访问控制策略中的写部分(如下所示)。

— 允许 employee_access 视图上的所有人更新 employee_information
grant update(employee_information) on bigco.employee_access to public;

— 允许 HR 部门在 mgmt_access 视图上更新 employee_information
grant update(department_id, employee_information) on bigco.mgmt_access to group d789;

— 允许 HR 部门在 mgmt_access 视图上执行删除
grant delete on bigco.mgmt_access to group d789;

  视图定义和权限相结合将有效地控制不同用户访问雇员信息的行为。下面的例子显示了通过 employee_accessmgmt_access这两个视图进行的不同数据修改所得的结果。请注意因为还没有人获得基本表上的授权,所以任何部门的任何雇员企图访问这些表都将失败。

— employee_access 例子

— 一般雇员(PBIRD)企图更新 employee_information
update bigco.employee_access set employee_information = ‘A different tidbit’;
DB20000I The SQL command completed successfully.
— 注意只更新了 PBIRD employee_information

— 一般雇员(PBIRD)企图更新 MARY 的 employee_information
update bigco.employee_access set employee_information = ‘A different tidbit too’
         where user_id = ‘MARY’;
SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a
query is an empty table. SQLSTATE=02000

— 一般雇员(PBIRD)企图更新 employee_id
update bigco.employee_access set employee_id = 77;
SQL0551N “PBIRD” does not have the privilege to perform operation “UPDATE”
on object “BIGCO.EMPLOYEE_ACCESS”. SQLSTATE=42501

— HR 部门的雇员(MARY)企图更新 MAX 的 employee_information
update bigco.employee_access set employee_information = ‘An exciting tidbit’ where user_id = ‘MAX’;
SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a
query is an empty table. SQLSTATE=02000

— HR 部门的雇员(MARY)企图插入一个新雇员
insert into bigco.employee_access values (99, ‘BARBARA’, ‘some fascinating tidbits’, ‘d111’);
SQL0551N “MARY” does not have the privilege to perform operation “INSERT”
on object “BIGCO.EMPLOYEE_ACCESS”. SQLSTATE=42501

— HR 部门的雇员(MARY)企图删除一个雇员
delete from bigco.employee_access where user_id = ‘MAX’;
SQL0551N “MARY” does not have the privilege to perform operation “DELETE”
on object “BIGCO.EMPLOYEE_ACCESS”. SQLSTATE=42501

  此时,我们已经实现了之前定义的几乎所有的访问控制策略。剩下的两条是使 HR 部门的雇员能向 employee表插入新的雇员记录,以及使管理部门的成员能更新 employee表的 employee_evaluation列。根据 mgmt_access视图中的定义,其中的 employee_evaluation列不能被更新,因为该列是通过 case 表达式的结果构造的。本例中,我们需要实现另一种方法来提供并控制指定的访问。

  除了需要更新视图中不可更新的列之外,还有其他几种情况也需要不同于传统的视图方法来提供写访问控制。这些情况包括:

只读视图(视图定义不允许通过该视图进行插入或修改操作)。
用户可读取的行集合与其可修改的集合不相同。

  这些情况需要用户采用与读访问不同的方法来修改数据。虽然通过用不同的定义为插入和更新操作创建新视图,仍然可以坚持限制表上的直接授权的原则,但用户只能用新视图执行写访问,原因是其定义不可能同时支持读访问控制。对于此类视图,只能授予用户视图上的写权限,而不能授予读权限。在有限的一些例子中,某些管理员或许喜欢采用更简洁的方法,就是向用户提供直接对表进行 INSERT、UPDATE 或 DELETE 操作的权限。不管采用何种方法授予写权限,都需要在目标表本身上实施写访问控制策略。这些情况下,在表上创建合适的 INSERT、UPDATE 或 DELETE 触发器可以为实施必需的写访问策略提供条件。触发器中使用的基本机制与视图定义中使用的相似:触发器定义中的 WHEN 从句用于判断激活该触发器的操作是否违反了访问策略,或者,至少用于判断在允许企图进行的操作继续之前是否还需对其进一步分析。触发器的主体将最终判断是否违反了访问策略,若有必要,它还将采取行动阻止访问操作。为了在将来修改时易于维护,触发器定义应该尽可能清楚且简单,并包含适当的注释。请注意这些策略实施触发器将十分有效,并能保护主题表免受所有企图进行的写访问的损害,无论该访问是直接对表进行的还是通过视图进行的。

  在决定如何通过触发器实现访问策略时,有许多可用的选项。首先,有必要确定在企图进行不正确或被限制的访问的时候,采用何种方法应对。是应该返回一条错误消息,还是应该使此受限的访问无效并允许其他处理继续进行?通过使用 SIGNAL SQLSTATE 语句,可从触发器中返回错误消息。触发器中的出错信号将回滚违反访问策略的语句,且该语句所做的任何修改都将从数据库中清除。某些情况下,修改了许多行,我们或许期望仅仅清除那些导致错误情况的行,并允许成功地完成语句的处理。为了允许语句继续执行而屏蔽掉错误行所需的技术取决于触发器的类型。下文讨论了对于每种触发器可能的无效操作。然而,一般说来,应该通过向用户发送错误消息来处理访问违规,而非仅使访问结果无效却无可见的出错提示。在处理访问企图时,触发器比视图要灵活得多。因此,在决定如何处理这些触发器中所发现的失败访问时,应该考虑是否记录此违法操作,用于审计以及将来可能继续的活动中。

  要实现针对不能通过视图中的 WITH CHECK OPTION 从句实现的更新操作的访问控制,我们推荐在主题表上定义一个 NO CASCADE BEFORE 类型的行级别的 UPDATE 触发器。在 UPDATE 语句执行的操作应用于每一行之前,这种触发器可对这些行逐行判断。通过适当调整 update 触发器中的判断内容,能使其只对表中指定列的更新敏感。这样做是为了尽可能最小化进行判断所带来的开销。在这种触发器中,为了使违规的 UPDATE 访问无效,您可以在让更新操作继续进行之前,以该行的旧值代替新值。而通过使用 REFERENCING 从句,可使新的和旧的行在触发器体中都可用。

  暂时回到本例中,为了让管理部门的成员可以更新employee表中的 employee_evaluation列,我们可以实现一个 UPDATE 触发器,与下例所示相似。它将使 HR 部门的雇员进行的非法更新无效。然而本例中,我们并不真正需要用到触发器,因为通过向管理组显式地授予 employee表的 employee_evaluation列上的 UPDATE 权限,就可以真正地实施访问控制策略了。而给出的 UPDATE 触发器的定义实际上只确保所提交的值在考核值的有效集合中。虽然这不是一个策略实施触发器,但其定义和使之无效的操作与策略实施触发器中所使用的相同。但其 WHEN 从句的定义是不同的,因为它是用于检测访问控制违规,而非错误的更新值。

— 允许管理部门更新 employee 表上的 employee_evaluation
grant update(employee_evaluation) on bigco.employee to group d666

— 在 bigco.employee 上创建一个 update 触发器,以实施更新访问控制
— 以便只有管理部门可以更新 employee_evaluation
create trigger update_control no cascade before update of employee_evaluation on

 

bigco.employee referencing old as old new as new for each row mode db2sql

— 如果是有效的 employee_evaluation 值
when (new.employee_evaluation not in (‘A111’,’B212’,’FFFF’,’B114’,’A32A’))
begin atomic

    — 用当前值取代新的值并允许处理继续进行
set new.employee_evaluation = old.employee_evaluation;
  end

  关于该实现如何应对不同的更新企图,下面的样例输出显示了这些例子:

— HR 部门的雇员(MARY)企图进行的更新
update bigco.employee set employee_evaluation = ‘FFFF’ where employee_id = 4;
SQL0551N “MARY” does not have the privilege to perform operation “UPDATE”
on object “BIGCO.EMPLOYEE”. SQLSTATE=42501

— 管理人员(HUGO)企图将所有记录更新为一个有效值
update bigco.employee set employee_evaluation = ‘jjjj’;
DB20000I The SQL command completed successfully.

— 管理人员(HUGO)读到的更新企图的结果
select employee_evaluation from bigco.mgmt_access
EMPLOYEE_EVALUATION
—————————-
A111
B212
FFFF
B114
  4 record(s) selected.

— 管理人员(HUGO)企图将所有记录更新为有效值
update bigco.employee set employee_evaluation = ‘FFFF’;
DB20000I The SQL command completed successfully.

— 管理人员(HUGO)读到的更新企图的结果
select employee_evaluation from bigco.mgmt_access
EMPLOYEE_EVALUATION
—————————-
FFFF
FFFF
FFFF
FFFF
  4 record(s) selected.

  要实现一个策略实施触发器来控制企图进行的插入访问,您可以定义行级别的 INSERT 触发器来检查非法访问。若要报告关于检测出访问违规的错误消息,出于性能考虑,我们推荐您使用 NO CASCADE BEFORE 触发器。仅取消插入操作,而不向用户发送错误消息会带来问题。因此,惟一的方法就是在通过使用 AFTER 类型的行级别触发器之后,删除新插入的行。使用 AFTER 触发器来取消插入,并不能阻止其他的 AFTER 触发器被激活。因此,如果存在其他的行级别 AFTER 触发器,数据库上的所有修改就并非都无效。

  本文开头定义的访问控制策略中,本例还未实现的最后一条就是,HR 部门的雇员可向 employee 表插入新的雇员。与更新能力一样,最好而且也最容易通过向 HR 部门的组 ID(即 d789)授予 employee 表上的 INSERT 权限来实现该插入能力。我们推荐此方法。然而,为了进行讨论,也可以通过如下方式实现,即向 PUBLIC 授予 INSERT 权限,并使用一个行级别的 INSERT 触发器来检测访问违规。

— 允许任何人在 employee 表上进行插入
grant insert on bigco.employee to public

— 在 bigco.employee 上创建一个 insert 触发器,以实施插入访问控制
— 以便只有 HR 部门的雇员可以插入新的雇员
create trigger insert_control no cascade before insert on bigco.employee

  for each row mode db2sql
— 如果用户不是 HR 部门的成员
  when (USER not in (select employee_userid
      from bigco.employee
where department_id = ‘d789’))
  begin atomic
    — signal an error
signal sqlstate ‘75001’ (‘Access denied’);
  end

  关于该实现如何应对不同的插入操作,下面的样例输出显示了这些例子:

— 一般雇员(PBIRD)企图插入一个新的雇员
insert into bigco.employee values (99, ‘BARBARA’, ‘some fascinating tidbits’, ‘FFFF’, ‘d111’);
SQL0438N Application raised error with diagnostic text: “Access denied”.
SQLSTATE=75001

— 管理人员(HUGO)企图插入一个新的雇员
insert into bigco.employee values (99, ‘BARBARA’, ‘some fascinating tidbits’, ‘FFFF’, ‘d111’);
SQL0438N Application raised error with diagnostic text: “Access denied”.
SQLSTATE=75001

— HR 部门的雇员(MARY)企图插入一个新的雇员
insert into bigco.employee values (99, ‘BARBARA’, ‘some fascinating tidbits’, ‘FFFF’, ‘d111’);
DB20000I The SQL command completed successfully.

  最后,要通过触发器实施删除访问控制策略,您仍需定义一个行级别的 DELETE 触发器,来检查企图进行的非法访问。若要报告访问违规错误,出于性能考虑,我们仍推荐您使用 NO CASCADE BEFORE 触发器。取消删除操作将产生试图取消非法的插入操作时相同的问题。因此,惟一的方法就是在通过使用 AFTER 类型的行级别触发器之后,重新向表插入已删除的行。与前面一样,它也无法阻止其他的 AFTER 触发器被激活。因此,如果存在其他的行级别 AFTER 触发器,数据库上的所有¿®改就并非都无效。

  结束语

  阅读文本并尝试对所提供的实现示例加以变化后,对于 DB2 Universal Database 中用于提供行和列级别的安全且可靠的访问控制的各种可用方法,您就应该有较好的基本理解了。

  为了确保一个成功的实现,需要:

1. 为读写访问创建其访问控制策略的清晰定义。
2. 请记住权限是第一道防线也是最好的防线!
3. 避免直接授予表上的权限(可能时)。
4. 向尽可能少但必要的人授予尽可能少的权限(在适当的地方使用列级别的权限)。
5. 确定哪种授权模式对您而言最好:动态 SQL 或静态 SQL,还是两者混合使用?
6. 使用视图来实现行与列的读访问控制策略。
7. 使用带有 WITH CHECK OPTION 的视图来实现写访问控制策略(可能时)。
8. 在不适合采用视图的情况下,使用行级别的 BEFORE 触发器来实施写访问控制策略。

  实现访问控制策略时,您还需考虑下列几个因素:

用文档记录策略!不仅记录在纸上,还应记录在用于定义视图和触发器的 SQL 中。
使之读和维护起来能尽可能清楚且简单。
考虑是否需要补充 DB2 Audit 工具(在实现策略实施触发器时)。

  有关于 DB2 中授权、视图定义或触发器定义等方面的各种选项和方法的进一步信息,请查阅随产品提供的 DB2 UDB 在线参考材料,以及 IBM Data Management 网站(www.ibm.com/software/data/db2)。

(责任编辑:IT教学网)

更多

推荐数据库文章