VPD策略功能

时间:2018-12-16 22:09:26

标签: sql oracle

我是oracle安全的新手,并且对虚拟专用数据库有疑问: 假设我有一个由名为“ PCM”的用户创建的名为“工资单”的下表

EMP_ID               DEPT                      TOTAL      TAXES
-------------------- -------------------- ---------- ----------
E1                   accounting                 2400        100 
E2                   sales                      2500         75 
E3                   research                   3000        110 
E4                   operations                 4200        120 
E5                   sales                      4800        130 
E6                   sales                      2500         75 
E7                   accounting                 5200        140 
E8                   accounting                 2700        105

现在我要实现的目标如下:拥有dept = accounting"的任何人都可以选择拥有dept != accounting的所有其他行,但是拥有dept != accounting的任何人都只能查看他/她的记录。 我的问题是:由于无法在表上应用select语句,因此我们限制对策略函数(即工资单)内部的访问,并且由于在(工资单)表上应用策略函数将应用于基于视图的任何视图在上面,解决这个问题的逻辑是什么?解决方案是什么,我写了以下内容“我以其他用户身份连接,而不是工资表的所有者,因此我以名为ANNE的用户身份连接”:

CREATE OR REPLACE CONTEXT payroll_ctx USING payroll_ctx_pkg;
CREATE OR REPLACE PACKAGE payroll_ctx_pkg IS 
  PROCEDURE set_dept;
 END;
/
CREATE OR REPLACE PACKAGE BODY payroll_ctx_pkg IS
  PROCEDURE set_dept
  AS
    v_dept varchar2(400);
  BEGIN
     SELECT dept INTO v_dept FROM PCM.PAYROLL
        WHERE EMP_ID = SYS_CONTEXT('USERENV', 'SESSION_USER');
     DBMS_SESSION.SET_CONTEXT('payroll_ctx', 'dept', v_dept);
  EXCEPTION
   WHEN NO_DATA_FOUND THEN
   DBMS_SESSION.SET_CONTEXT('payroll_ctx', 'dept', 'E0');
  END set_dept;
END;
/

考虑到要尝试访问该表的用户现在拥有emp_id列的名称,

CREATE TRIGGER set_dept_trig AFTER LOGON ON DATABASE
 BEGIN
  ANNE.payroll_ctx_pkg.set_dept;
 END;
/

现在是问题(我知道这是错误的),但是找不到解决方案:

create or replace function sec_fun (p_schema varchar2, p_obj varchar2)
return varchar2
as
    vv_dept varchar2(400);
    payroll_pred varchar2(400);
begin
    payroll_pred := '1=2';
    vv_dept := SYS_CONTEXT('payroll_ctx', 'dept');
    if (vv_dept != 'accounting') then
        payroll_pred := 'DEPT =''' || vv_dept ||'''';
    else
        payroll_pred:='DEPT !=''' || vv_dept ||'''';
    end if;
   return payroll_pred;
end;
/

然后:

BEGIN
 DBMS_RLS.ADD_POLICY (
  object_schema    => 'PCM', 
  object_name      => 'PAYROLL', 
  policy_name      => 'payroll_policy', 
  function_schema  => 'ANNE',
  policy_function  => 'sec_fun',
  statement_types  => 'select');
END;
/

以及用户E1尝试从工资单中选择时出现的错误消息:

no rows selected

我做错了什么? 我已根据以下答案编辑了问题和结果。

1 个答案:

答案 0 :(得分:0)

我将做出一些猜测,但是如果没有一个能解决您的问题,您将需要查找数据库提供的诊断信息。 ORA-28112将在数据库服务器上的user_dump_dest目录中生成跟踪文件。这应该为您提供诊断和修复策略代码中的任何错误所需的所有信息。如果您无权访问该目录,则需要请您友好的DBA / Sysadmin团队提供帮助。

所以,猜测。

您正在将vv_dept设置为'dept = SYS_CONTEXT(''payroll_ctx'', ''dept'')'的字符串。在IF块中,将其连接到WHERE子句过滤器。因此,您的最终字符串实际上是:

DEPT =dept = SYS_CONTEXT('payroll_ctx', 'dept')

(它永远不会执行DEPT !=分支,因为您正在将该字符串分配给vv_dept而不执行它,因此vv_dept永远不能等于'Accounting'。)

这显然是错误的,因此Oracle推出ORA-28112也就不足为奇了。幸运的是,解决方案同样清晰:只需整理vv_dept的分配即可:

vv_dept := SYS_CONTEXT('payroll_ctx', 'dept');

要查看的另一件事是策略本身的格式。您正在测试字符串的相等性,因此需要将其用引号引起来以形成有效的SQL:

        payroll_pred := 'DEPT =''' || vv_dept ||'''';

显然,其他分支也是如此。

要考虑的另一件事。如果您以不在payroll表中的用户身份登录(不确定是否可行),请执行以下步骤:

WHEN NO_DATA_FOUND THEN NULL;

因此,该会话没有上下文设置,这意味着如果用户随后尝试查询payroll,则策略功能将失败。更好的方法是将dept的上下文设置为空,然后在策略中为vv_dept is null添加一个适用1=2或类似方法的测试。

  

ORA-06502:PL / SQL:数字或值错误:字符串缓冲区太小

如果您运行已发布的sec_fun代码,肯定会得到,因为您将vv_dept定义为varchar2(20),并且分配的字符串超过了20个字符。但是您说您正在运行我建议的修订,所以不是那样的。 payroll.dept的大小是多少?

最后一个字

VPD策略只是动态SQL的一种特殊情况,核心教义仍然存在:动态SQL很难,因为它将编译错误转换为运行时错误。同样,主要的调试工具仍在编写执行句柄,该句柄将整个汇编的字符串记录到表(或文件)中。


顺便说一句,我认为业务逻辑中存在缺陷。目前,没有人可以查看会计部门的记录(具有EXEMPT ACCESS POLICY特权的高级用户除外)。