首页 > 解决方案 > VPD 政策功能

问题描述

我是 Oracle 安全新手,我有一个关于虚拟专用数据库的问题:假设我有一个名为“Payroll”的表,由名为“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

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

标签: sqloracle

解决方案


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

所以,猜测。

您正在设置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。更好的方法是使用 null for 设置上下文dept,然后在策略中添加vv_dept is null适用的测试1=2或类似的东西。

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

好吧,如果您运行发布的sec_fun代码,您肯定会明白这一点,因为您定义vv_deptvarchar2(20)并且分配的字符串长度超过 20 个字符。但是你说你正在运行我建议的修订,所以不可能这样。的大小是payroll.dept多少?

遗言。

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


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


推荐阅读