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

