You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL中BEFORE INSERT触发器绕过列插入权限问题咨询

问题:触发器为何能修改无插入权限的列?

我有一张启用了行级权限(ROW LEVEL SECURITY)的表,为用户myUser配置了插入策略,仅授予其SomeData列的插入权限。新增CreatedById列用于记录插入者ID,通过BEFORE INSERT触发器自动填充。

相关表结构及配置如下:

CREATE TABLE "MyTable" (
"MyId" SERIAL PRIMARY KEY,
"SomeData" NOT NULL TEXT,
"CreatedById" UUID NOT NULL
);

-- Trigger Function
CREATE FUNCTION "SetCreatedById_tr"() RETURNS TRIGGER AS $$
BEGIN
    IF current_user_id() IS NULL THEN
      RAISE EXCEPTION 'current_user_id() is NULL';
    END IF;

    NEW."CreatedById" = current_user_id(); -- Why does this work when myUser doesn't have an insert policy for "CreatedById"
    RETURN NEW;
END;
$$ LANGUAGE plpgsql VOLATILE SECURITY INVOKER;

-- BEFORE INSERT Trigger
CREATE TRIGGER "CreatedById_tr" 
    BEFORE INSERT ON "MyTable" 
    FOR EACH ROW EXECUTE FUNCTION "SetCreatedById_tr"();

-- ROW LEVEL SECURITY
ALTER TABLE "MyTable" ENABLE ROW LEVEL SECURITY;

-- POLICIES
CREATE POLICY "myUser_Insert_MyTable" ON "MyTable" FOR INSERT TO myUser WITH CHECK ("UserId" = current_user_id());
GRANT INSERT ("SomeData") ON TABLE "MyTable" TO myUser;
GRANT USAGE ON SEQUENCE "MyTable_MyId_seq" TO myUser;

我未将CreatedById列加入GRANT的插入权限列表,但触发器却能成功填充该列,这令我困惑:为何触发器修改无权限列时插入未失败?SECURITY INVOKER属性不应让触发器受用户权限约束吗?

当myUser执行:

INSERT INTO "MyTable" ("SomeData") VALUES (input.*) RETURNING * INTO newRow;

触发器成功插入CreatedById;但执行直接指定CreatedById的INSERT语句:

INSERT INTO "MyTable" ("SomeData", "CreatedById") VALUES (input.*) RETURNING * INTO newRow;

却出现预期的"permission denied for table MyTable"错误,请问这一现象的原因是什么?


解答
  • 权限检查的对象和时机差异:
    PostgreSQL执行INSERT时,首先只检查用户显式指定要插入的列是否在授权范围内。如果用户只写了SomeData,数据库只会验证myUser对SomeData的插入权限,这一步没问题。
    触发器是在这次权限检查通过后、实际写入数据前执行的。虽然触发器是SECURITY INVOKER(以myUser身份运行),但它修改NEW行的操作属于数据库内部的行调整,不是用户主动发起的列插入操作,所以不受用户对该列的插入权限限制——用户根本没尝试直接插这个列,只是触发器在补值。

  • 直接指定列触发权限检查:
    当用户在INSERT语句里显式列出CreatedById并赋值时,数据库会把这个列纳入权限检查范围。由于你只给myUser授权了SomeData的插入权限,这一步就会触发权限拒绝错误。

  • 行级安全与列权限是独立体系:
    你的插入策略里的WITH CHECK条件(这里表结构没有UserId列,应该是笔误写了CreatedById?)控制的是哪些行允许插入,而GRANT语句管的是用户对列的操作权限,两者互不影响,行级安全不会替代列权限检查。


内容的提问来源于stack exchange,提问作者Grumpkin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 12:12:40