security_barrier视图必须显式启用且依赖LEAKPROOF函数,否则权限控制可能被绕过;需用WITH (security_barrier = true)创建,确保所有调用函数标记为LEAKPROOF,并避免与RLS或WITH CHECK OPTION冲突。

security_barrier 视图不是“开了就安全”,而是必须显式启用、且依赖底层函数是否 LEAKPROOF,否则权限控制可能被绕过。
创建视图时必须显式声明 WITH (security_barrier = true)
PostgreSQL 默认不启用安全屏障,即使你用了 WHERE 过滤行,普通视图仍可能被攻击者用低成本函数(如自定义 attack())触发提前执行,从而泄露被过滤的数据。
security_barrier = true 强制 PostgreSQL 将视图的过滤条件作为“不可穿透的屏障”优先执行,阻断外部谓词下推干扰。
注意:该选项仅在 PostgreSQL 9.5+ 支持,且不能和 WITH CHECK OPTION 共存。
检查并确保所有依赖函数标记为 LEAKPROOF
如果视图里调用了自定义函数(比如 to_tsvector('english', body) 或你写的 mask_phone()),而它没被声明为 LEAKPROOF,PostgreSQL 会拒绝将其用于 security_barrier 视图——要么报错,要么降级为普通视图,失去保护能力。
验证方式:
SELECT proname, proleakproof FROM pg_proc WHERE proname IN ('to_tsvector', 'your_function_name');若 proleakproof 为 false,需重建函数并加 LEAKPROOF 属性(仅超级用户可设):
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
CREATE OR REPLACE FUNCTION mask_phone(text) RETURNS text AS $$...$$ LANGUAGE sql LEAKPROOF;
- 非
LEAKPROOF函数可能暴露中间结果(如通过RAISE NOTICE) - 内置函数如
length()、upper()通常是LEAKPROOF;但全文检索类函数默认不是 - 用
EXPLAIN (VERBOSE)查执行计划,确认没有意外出现Row Security Policy或谓词重排
避免与 RLS 表混用时的策略冲突
当视图底层表已启用了行级安全策略(RLS),而你又建了 security_barrier 视图,两者可能叠加或互斥:
- 若 RLS 策略由视图 owner 执行(即 security definer 模式),它会在视图过滤前生效,可能提前截断数据;
- 若视图设为 security_invoker = on,则 RLS 会按调用者身份检查,但此时 security_barrier 仍有效——前提是函数都 LEAKPROOF。
关键判断点:
- 用
\d+ view_name看视图属性是否含security_barrier - 用
SELECT * FROM pg_views WHERE viewname = 'xxx';确认定义未被隐式改写 - 测试时务必用无权限的普通用户连接,而非
postgres或 owner 账户
为什么 security_invoker = on 会让视图更“严”,但也更易报错
默认视图是 security definer:以 owner 权限检查底层表访问权,所以只要给用户 SELECT 视图权限,ta 就能查到数据。
一旦执行 ALTER VIEW v SET (security_invoker = on),PostgreSQL 改为用调用者权限检查——这意味着用户必须对视图里所有基础表都有 SELECT 权限,否则直接报 permission denied for table xxx。
这不是 bug,是设计意图:它把权限控制粒度从“视图层”下沉到“表层”,配合 security_barrier 可构建双重防护,但代价是权限配置变复杂。
真正容易被忽略的是:安全屏障只防“谓词下推攻击”,不防“权限误配”或“函数侧信道”。哪怕你写了完美的 WITH (security_barrier = true),只要漏掉一个 LEAKPROOF 函数,或者忘了关掉某个用户的 USAGE on schema,整套防线就形同虚设。

















