代码之家  ›  专栏  ›  技术社区  ›  Lenijas

安全定义器不在内部工作,而不是触发?

  •  2
  • Lenijas  · 技术社区  · 7 年前

    我有一个PostgreSQL 9.5数据库,其中包含:

    • 表格测试。存储字段可能值的域(int-id,text-value)。此数据是动态的。
    • 表格测试。域字段引用到测试的表(id int,domainn text)。域表。
    • 视图测试。view\u域,它是测试的视图。领域

    我在视图上定义了一个带有安全定义器选项的替代触发器。此触发器更新表测试。领域问题是,尽管此触发器是作为用户“系统”执行的,但表测试上的引用更新。表由调用程序用户执行。

    下面是一个示例,如果作为postgres执行,我希望得到“user system”作为错误,而不是“user postgres”

    drop schema IF EXISTS test  cascade;
    create schema test;
    
    
    create function test.modified() returns trigger as
    $$
    BEGIN
        raise exception 'user %', ' '||current_user;
    END
    $$
    language plpgsql;
    
    set role system;
    
    create function test.insert_with_system() returns trigger as
    $$
    DECLARE
     valor text;
    BEGIN
    
        --raise exception 'user %', ' '||current_user;
        update test.domain set value =  ''||new.value where id =  new.id;
    
    END
    $$ language plpgsql security definer;
    
    reset role;
    
    CREATE table test.domain(
    
    id int primary key,
    value text unique
    );
    
    create view test.domain_view as select * from test.domain;
    
    create table test.table(
    id int primary key,
    domainn text
    );
    
    alter table test.table add foreign key (domainn)
       references test.domain(value) on delete restrict on update cascade;
    
    create trigger test_trigger before insert or update or delete on test.table
       for each row execute procedure test.modified();
    
    create trigger instead_ins INSTEAD OF update or delete on test.domain_view 
       for each row execute procedure test.insert_with_system();
    
    
    insert into test.domain(id, value) values(1,'one');
    
    alter table test.table  DISABLE TRIGGER all;
    insert into test.table(id, domainn) values (0,'one');
    alter table test.table enable TRIGGER all;
    
    update test.domain_view set value = 'two';
    select * from test.table;
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Laurenz Albe    7 年前

    级联更新始终在引用表所有者的安全上下文中运行( test.table 在您的示例中)。

    看见 ri_PerformCheck 在里面 src/backend/utils/adt/ri_triggers.c :

    /*
     * Use the query type code to determine whether the query is run against
     * the PK or FK table; we'll do the check as that table's owner
     */
    if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
        query_rel = pk_rel;
    else
        query_rel = fk_rel;
    
    ...
    
    /* Switch to proper UID to perform check as */
    GetUserIdAndSecContext(&save_userid, &save_sec_context);
    SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
                           save_sec_context | SECURITY_LOCAL_USERID_CHANGE |
                           SECURITY_NOFORCE_RLS);
    

    我试图遵循代码的起源,而行为似乎起源于提交 465cf168eb6151275016486fe2d2c629fed967ca

    在黑客档案中搜索相关讨论,我发现 this

    因此,据我所知,这种行为试图避免以下情况:

    • 使用者 A 拥有 atable 和赠款 REFERENCES 在该表上发送给用户 B

    • 使用者 B 拥有 btable 并将外键添加到 A表格 具有 ON UPDATE OR DELETE CASCADE 使用者 A. 对没有权限 B表格

    • 使用者 A. 尝试更新或删除中的行 A表格 这将级联到 B表格 并失败,出现“权限被拒绝”错误。

    我个人不确定目前的行为是好是坏,但我可以看到这一点,这将是令人惊讶的 A. '无法修改她拥有的表。