内容提要
PostgreSQL行级安全(RLS)可限制AI代理仅访问特定租户数据,但需非所有者角色连接、启用FORCE RLS、为每项操作定义显式策略,并通过实际代理登录测试。本文提供可复现的两租户示例,涵盖权限授予、策略创建、允许/拒绝行为验证,并强调避免所有者绕过RLS的测试陷阱,确保安全隔离。
延伸解读
RLS的边界:不是万能隔离
文章明确指出,RLS只控制行级访问,不替代标准权限。TRUNCATE、REFERENCES等表级操作不受RLS约束,超级用户和BYPASSRLS角色始终绕过RLS。表所有者默认绕过RLS,除非启用FORCE ROW LEVEL SECURITY。因此,RLS应视为数据库边界之一,需与最小权限授予、非所有者角色等配合,才能构建有效的多租户隔离。
测试陷阱:避免所有者绕过
最常见的误判是使用表所有者或超级用户身份测试RLS,因为所有者默认绕过策略,导致测试结果看似RLS失效。文章强调,必须通过实际代理登录角色进行验证,并检查角色属性(如rolbypassrls)和RLS标志。同时,应建立回归测试矩阵,在角色、策略、迁移等变更后重复执行,确保安全配置持续有效。
策略组合与身份绑定
RLS策略默认是permissive,多个permissive策略用OR组合,可能扩大访问范围;restrictive策略用AND组合,可进一步限制。此外,若策略依赖current_setting获取租户ID,需确保该值由可信应用层设置,而非客户端可篡改。否则,AI代理可自行设置租户值,绕过隔离。建议使用固定角色和固定策略范围,或绑定事务级身份。
Q&A
如何为AI代理配置PostgreSQL行级安全(RLS)?
为AI代理配置RLS的关键步骤包括:使用非所有者角色连接(NOBYPASSRLS),启用表的行级安全并强制(FORCE ROW LEVEL SECURITY),为每个允许的操作(SELECT、INSERT、UPDATE)创建显式策略,并通过实际代理登录测试。同时,仅授予必要的表权限,避免授予DELETE、TRUNCATE等不需要的操作。
为什么测试RLS时不能使用表所有者或超级用户身份?
因为表所有者通常绕过RLS,除非表设置了FORCE ROW LEVEL SECURITY;超级用户和具有BYPASSRLS属性的角色总是绕过RLS。如果使用这些身份测试,即使策略正确,也可能看到所有行,导致误判RLS未生效。因此,必须使用代理实际使用的非所有者角色登录进行测试。
PostgreSQL行级安全(RLS)能替代标准权限控制吗?
不能。RLS是额外的数据库边界,不是标准权限的替代品。标准权限(如SELECT、INSERT等)决定角色能否对表执行操作,而RLS决定这些操作能访问哪些行。RLS策略本身不授予表访问权限,角色仍需具备相应的表级权限。
在RLS策略中,USING和WITH CHECK表达式有什么区别?
USING表达式用于决定现有行是否对命令可见或可操作(如SELECT、UPDATE、DELETE的目标行),而WITH CHECK表达式用于检查INSERT或UPDATE产生的新行值是否满足条件。如果新行不满足WITH CHECK,操作会报错。对于UPDATE,USING防止选择其他租户的行,WITH CHECK防止将行修改为其他租户。
如何验证RLS配置是否正确?
通过实际代理角色登录,执行一系列允许和拒绝的测试:确认角色属性(非超级用户、无BYPASSRLS),验证只能看到本租户的行,尝试跨租户更新(应影响0行)、跨租户插入(应报错)、租户变更(应报错)、未授权操作(如DELETE,应权限拒绝)。同时,检查pg_policies视图确认策略定义,并定期重复测试。
RLS有哪些局限性?
RLS的局限性包括:超级用户和BYPASSRLS角色始终绕过;表所有者默认绕过(除非FORCE);TRUNCATE和REFERENCES不受RLS控制;唯一、主键和外键约束检查可能绕过RLS并泄露行存在性;策略中查询其他表可能产生竞态条件;设置row_security=off不会绕过RLS,而是使查询报错。因此,RLS需与其他安全措施结合。
如何防止AI代理通过设置自定义参数绕过RLS?
如果策略使用current_setting读取租户ID,必须确保该值由可信应用层从认证身份派生,并在事务内设置,且防止SQL调用者自行设置。对于直接数据库连接,最好使用固定角色和固定策略范围,如本文示例。对于连接池架构,应在可信代码中绑定租户上下文,使用事务局部状态,并测试连接复用。