安全屏障视图需显式声明with (security_barrier = true)且所有函数标记leakproof,否则普通视图会因谓词下推导致raise notice等函数泄露被where过滤的行数据。

安全屏障视图不是默认开启的,不显式声明 WITH (security_barrier = true) 就等于没设——普通视图完全无法防御侧信道攻击。
为什么普通视图会泄露被 WHERE 过滤的行
PostgreSQL 默认把视图当作查询重写规则,外部条件(比如自定义函数)可能被下推到视图内部执行。一旦用户能创建低成本函数(如含 RAISE NOTICE 的 attack(id, info)),就能在视图的 WHERE groupid = 2 过滤前,把所有原始行数据“打印”出来。
- 现象:对
v_userinfo执行SELECT * FROM v_userinfo WHERE attack(id, info),NOTICE中出现 id=3、4、5 的数据 - 原因:视图未启用安全屏障,优化器把
attack()下推进去了 - 验证方式:
\d+ v_userinfo输出中必须含Security barrier: yes;否则就是普通视图
创建 SECURITY BARRIER 视图的硬性步骤
缺一不可,少一个就降级为普通视图,防护失效。
- 必须显式写
CREATE VIEW ... WITH (security_barrier = true) AS ...——CREATE VIEW ... AS ...不带该选项,永远是普通视图 - 视图定义中所有调用的函数(包括内置函数)必须标记为
LEAKPROOF,否则 PostgreSQL 要么报错,要么静默降级 - 检查函数是否 LEAKPROOF:
SELECT proname, proleakproof FROM pg_proc WHERE proname IN ('to_tsvector', 'mask_phone');,结果为false就得重建 - 重建示例:
CREATE OR REPLACE FUNCTION mask_phone(text) RETURNS text AS $$ SELECT regexp_replace($1,'(\d{3})\d{4}(\d{4})','\1****\2'); $$ LANGUAGE sql LEAKPROOF;
LEAKPROOF 函数的坑和限制
标错 LEAKPROOF 比不标更危险:它会让 PostgreSQL 强制提前执行该函数,哪怕它很慢,甚至触发副作用。
- 绝不能标
LEAKPROOF的函数:RAISE NOTICE、pg_sleep()、访问外部 HTTP 或文件系统、依赖会话变量但未校验类型(如current_setting('app.user_id')未加TRUE参数) - 内置函数中,
upper()、length()、substr()通常是 LEAKPROOF;但to_tsvector()、jsonb_path_match()、current_setting()默认都不是 - 性能影响:LEAKPROOF 函数会被强制在视图过滤前执行,如果函数本身耗时高(如全文检索解析大字段),整个查询延迟会明显上升
与 RLS、WITH CHECK OPTION 的冲突
安全屏障视图和某些 PostgreSQL 安全机制天生互斥,混用会直接失败或行为异常。
-
WITH (security_barrier = true)和WITH CHECK OPTION不能共存:建视图时 PostgreSQL 直接报错ERROR: security_barrier view cannot have WITH CHECK OPTION - 与 RLS 表混用时需谨慎:若底层表已启 RLS,而视图 owner 权限高于调用者,RLS 策略可能在视图过滤前就截断数据;若视图设
security_invoker = on,RLS 仍生效,但前提是所有函数都 LEAKPROOF - 测试必须用无权限的普通用户连接——用
postgres或视图 owner 登录,根本测不出问题
真正起作用的安全屏障,是三者同时成立:显式声明 security_barrier = true、所有函数 LEAKPROOF、且调用者无底层表直查权限。漏掉任何一层,攻击者都可能绕过视图的 WHERE 条件拿到不该看的数据。










