行级安全
这是一个开发页面,讨论尚未包含在 PostgreSQL 中的功能部分或稍后将包含在 PostgreSQL 中的功能部分的进行中讨论。这不是关于如何使用现有功能的指南。
行级安全已 提交至 PostgreSQL 9.5
行级安全
此页面用于讨论 PostgreSQL 中行安全 (RS) 的实现。行安全以前称为行级安全 (RLS)。“行安全”通过诸如“虚拟私有数据库”、“细粒度安全”等名称广为人知。它是 基于标签的安全 和 强制访问控制 (MAC) 得以建立的基础。
行安全是一项安全功能,允许用户向其他人授予其表中数据子集的访问权限。传统的 RDBMS 权限系统不会区分表中的各行,因此访问权限是所有或无。在表上授权选择将允许用户访问该表的全部行。行安全以单独行的形式对此进行补充,增加了额外的访问控制层级,因此可以由数据库指定和实施“管理人员只可以查看向他们报告的那些员工的敏感员工信息”等要求。
提交节日、补丁和状态
还有 一块 Trello 留言板 用于跟踪行安全方面的公开事项。
基本原理
根据 ISO/IEC27001(信息安全管理)的定义,信息安全功能的设计目标是确保信息资产的机密性、完整性和可用性。简而言之,这些信息通常被称为 C.I.A.
访问控制有助于提高机密性和完整性。访问控制根据访问控制系统中配置的规则,阻止无权限的用户读取或写入信息资产。信息或数据本身没有特别有形的形式,因此它必须存储在对象中。通常,访问控制功能允许或禁止用户访问包含信息或数据的对象。RS 的目的是允许对对象中的信息进行更细粒度的控制。
例如,常见的授予/撤回机制根据访问控制列表控制对指定数据库对象的访问,但它们不允许更细粒度的控制。这种粗略的访问控制对于多租户、托管和高度安全敏感的环境可能存在问题。
用例
- PCI 合规性实施
- 分类环境
- 共享托管/多租户应用程序
设计目标
行级安全功能的目的是阻止用户(不是数据库角色,仅仅是用户)访问非特权行。访问是指信息流的两个不同方向 - (1) 为了机密性,数据读取(行 => 用户),以及 (2) 为了数据完整性,数据写入(用户 => 行)。
实现此目的最简单的方式是将这些访问限制表达为与其他 SQL 类似的 SQL 谓词WHERE子句。此类谓词可以是子查询甚至 C 函数,以允许最大的灵活性。
总的来说,RS 禁止用户读取和写入不满足行访问谓词(也称为行安全策略)的行。如果要支持按命令配置,则要检查的行安全策略将取决于命令;目前尚未就按命令策略是否恰当在邮件列表讨论中达成任何明确结论。
RS 旨在让精细的行安全配置尽可能变得简单、方便。它需要一致、清晰、且用户尽可能容易做正确的事情。
RS 策略如何影响各个 SQL 命令
PostgreSQL 有许多 SQL 命令允许用户直接访问数据库行
- SELECT, COPY TO,带 DMLRETURNING子句
- DML -INSERT, UPDATE和DELETE, COPY FROM
对于SELECT(数据读取,无写入),通过应用已配置的行安全谓词,对不符合已配置的行安全策略的任何行,过滤掉非 RS 豁免用户。
对于UPDATE或DELETE(数据写入),RLS 禁止未授权行显示为修改候选;这意味着这些命令的行安全策略也应应用在表扫描阶段。DML 可能还需要检查写入权限。
- 对于UPDATE应用读取谓词以限制哪些行UPDATE可以查看以影响。写入谓词为UPDATE应用于被UPDATE.
- 对于INSERT替换的每个元组,不要应用任何读取谓词,因为不会读取目标表。写入谓词为INSERT应用于每个要插入的元组。
- 对于DELETE应用读取谓词以限制哪些行DELETE能看到的。写入谓词为DELETE应用于每个要删除的元组。
其他行访问
还有一些间接方法可以查看行或必须防止查看的行部分或明确记录这些方法
- 当测试行时会泄露信息的谓词函数。RS 通过在运行任何用户谓词之前强制执行 RS 谓词来防止这种情况。
- CREATE INDEX在一个表达式/函数或用户定义类型上——这可以通过恶意操作符/函数来泄露行。RS 目前不防止这种情况,但只有表所有者或超级用户可以CREATE INDEX在一个表中。这仅与 MAC 相关,因此它超出了第一代 RS 的范围,但需要文档。
- CREATE TRIGGER带用户定义触发器函数。当通过引用完整性约束调用触发器时,这会泄露信息。除了在 MAC 下,这仅关系到授予TRIGGER对一个表拥有权利的非表所有者。需要文档说明TRIGGER权利允许绕过 RS。
- TRUNCATE在 MAC 下或对非所有者有TRUNCATE权限,TRUNCATE此声明允许用户绕过DELETE访问规则,尽管它提供不了读取权限。需要说明文档。
- 下列内容FOREIGN KEY侧通道。用户可以通过测试 DML 操作是否会在与受保护的表存在外键的表上取得成功,从而探查键的存在性。需要说明文档 - 建议对 RS 表之间的 FK 关系使用合成键或根本不使用外键。
- FOREIGN KEY联级 -ON UPDATE CASCADE, ON DELETE CASCADE和ON DELETE SET NULL。这些内容允许用户修改他们不应该能够修改的 RS 受保护表中的行。需要说明文档 - 不要在 RS 表之间的 FK 上设置联级,除非这是预期的作用。
- pg_catalog.pg_statistic包含了列中出现频率最高的值的直方图。只有超级用户能够访问它,或者在明确GRANT使用时, 因此除非在使用 MAC 的情况下,它都不会受到关注。需要说明文档。
- ON UPDATE CASCADE, ON DELETE CASCADE和ON DELETE SET NULL将运行在目标表上定义的触发器和规则。这些可能由用户定义,并且必须受到行安全性的影响,即使外键检查其本身不受到影响。待办事项。
行安全用户界面
行安全的权限
为了防止恶意用户定义的谓词函数利用超级用户,不对拥有OVERRIDE ROW SECURITY权限的用户(默认情况下为超级用户以及所有外键、唯一性约束和排除约束检查)执行行安全策略。在 C 级别注入策略的行安全扩展可以選擇忽略OVERRIDE ROW SECURITY;他们被认为是可信的。(当前没有名为OVERRIDE ROW SECURITY的数据库权限,它是隐式的,但这种GRANT能够的权限应该在未来添加)。
只有拥有SET ROW SECURITY权限的用户可以对表应用行安全权限。表所有者或超级用户可以在表上设置行安全性。类似于OVERRIDE ROW SECURITY,这当前是隐式权限。
行级安全不会覆盖现有的GRANT权限,它添加了一种更精细的控制级别。例如,设置ROW SECURITY FOR SELECT允许给定用户行会仅仅当该用户同时对相关列或表拥有SELECT权限时授予该用户访问权。
设置行安全的 SQL 命令
行安全策略由拥有ROW SECURITY权限(默认情况下为表所有者和超级用户)的用户使用以下语法设置:
(设计中的此部分仍处于不稳定状态)
ALTER TABLE <relname> SET ROW SECURITY FOR <privilege> TO (<expression>); ALTER TABLE <relname> RESET ROW SECURITY FOR <privilege>;
<privilege> 可以为以下之一:ALL、SELECT、INSERT、UPDATE、DELETE。初始实现可能仅支持“ALL”。
“ALL”只会为所有命令定义相同的策略。稍后的只更新单个命令的命令只会替换此命令的策略,而不会同时更改通过最初的“ALL”设置的其他命令的策略。重置也是如此。
若对于给定的命令类型尚未设置行安全策略(或者已重置该命令类型的安全策略),则不会应用任何行级安全。
问题和讨论
实施细节
需要一种安全的方式来运行外键检查和级联,使其免于 RLS,而不使用触发器和规则及免于 RLS 的用户自定义代码。
表继承
我们需要决定 RS 约束如何应用于继承。大多数约束和安全设置只直接影响目标表,而不影响父/子表。
出于实施原因,我们可能需要在父对子表中也应用 RS 谓词。这样可能会导致查询父表时看到不同的行,而当查询子表时看到的又是另一组不同的行。提案
- 不会对父查询应用子谓词。子谓词可能仅引用子中存在的行,并且对所有行而言没有意义。
- 当通过父表进行查询时,会将父谓词*应用*于子表。当直接查询子表时不会应用该谓词,相反会使用子谓词。
这可确保在任何情况下都不会添加两个谓词。
邮件列表中对此主题进行了非常详细的讨论;请参阅线程“Prohibit row security + inheritance in 9.4”。
转储和重新加载
必须转储和重新加载行安全策略,并且不受以下因素的影响:search_path当时的设置。pg_dump将需要得到加强才能使用的新变体pg_get_expr从pg_catalog.pg_rowsecurity中提取行安全表达式时,在架构方面对表达式进行充分限定。待办事项。
必须能够使用忽略行安全策略的一致备份,同时对pg_dump行约束COPY否则。
pg_dump决不能运行由普通用户定义的不受信任的行安全策略。但是,还希望允许在没有超级用户权限的情况下运行转储(我们已经有了太多只针对超级用户的功能)。因此,我们需要一个免除行安全检查的权限。
每个命令的安全策略
每个命令策略面临的一个挑战是 RETURNING,例如 UPDATE ... RETURNING 实际上也是 SELECT。因此,我们确实需要一个策略来控制可以读取的内容,另一个策略来控制可以写入的内容。最好为每个命令分别设置写策略;例如,您可能希望允许插入与允许更新或删除的不同行集。
非对称行安全策略可能会使用UPDATE或DELETE具有RETURNING子句或泄露函数的WHERE条款。因此,必须对 DML 执行的表扫描强制执行读取策略,除非 DML (a) 没有RETURNING条款,而且 (b) 仅调用防泄漏函数。UPDATE和DELETE不应该基于WHERE条款中函数的属性或RETURNING条款的存在性/不存在性影响不同的行集,因此,此时必须始终应用行安全性读取策略。如果您要允许删除/更新读取策略中不可见的行,则需要使用安全定义函数或其他绕过方法。
请注意,应用于UPDATE的读取策略不会限制最终值,只影响受影响的原始值。请参阅下面的#编写器端检查部分了解相关信息。
为所有命令设定单一策略(如当前行安全性补丁程序中实施的那样)会阻止实施经典的“分类”安全性模型,在该模型中您只能读取当前会话中相同或更低密级的行,但只能编写具有相同分类的行,而且只能删除具有未分类分类的行。
编写器端检查
用户可以通过使用没有其他核心代码的前行触发器在INSERT, UPDATE或DELETE命令中限制新的元组来实施编写器端检查。但是,这会增加在写入检查政策与读取检查政策中保持政策同步的难度。
因此,计划自动创建每个行的插入前或更新前或删除前触触发器就像当前对外部键执行操作的方式一样。这还需要额外的依赖关系管理。TO DO
最少核心功能集
- 仅在表扫描时应用检查。如果需要编写器端检查,用户可以使用触发器执行操作,即使它需要复杂的设置,并且难以维护。
- 可以在表中配置唯一的安全性策略。即使 RLS 设计允许每个命令使用策略,我们也需要调查非对称策略是否无害。
- 检查的外部键豁免
- 超级用户豁免
- 适当的依赖项跟踪
- 对策略组之类的未来增强功能持友好态度
以前的 RLS 讨论
文章/现有 RLS 实施的文档
其他供应商支持
- 可打开或关闭的多行安全性策略;以及
- 行安全性策略组,以打开或关闭策略集,关联用户和策略;
- 基于标签的安全性、自动列;
- 行安全性策略豁免的可委派权利
- 行安全性策略设置的可委派权利
- 表索引上的行安全性策略
- 安全会话变量,用于快速行安全性检查
Oracle
- Oracle VPD 文档(又称“细粒度安全性”)
- Oracle 标签安全文档
SQL Server
DB2
Teradata
- Teradata 14 安全管理(针对基于标签实现的 Teradata 行级安全)
实现的组件
- 关系中的记录谓词(针对 pg_rowsecurity 的目录更改)
- 针对控制谓词设置的语法和实用程序功能更改
- 在查询规划过程中应用谓词
- 确保谓词在任何不受信任的用户代码之前运行,以防止恶意谓词泄露
- 防止 RS 谓词以其他用户 ID 运行以提升权限
- 在 pg_depend 中设置对列、函数等项的依赖关系
- 解决门户 UID 更改问题、重新规划问题
针对基于标签的安全性
- 对超级用户强制执行谓词
- 提供完整逻辑转储的工具
- 自动创建用于行标签的隐藏列
- 在目录中跟踪哪些列是安全列
问题
- 秘密通道
- 如果我们尝试插入违反 PK 约束的值,我们可以从错误中推测出不可见的 PK。
- 外键关系的情况下存在相同的问题
- 其他具有行级安全性的数据库没有解决此问题,因此我们是否真正需要这么做尚不清楚。请参阅 Oracle VPD 文档。
- 没有合理且一致的模型来使带有不同标签/行安全策略的表之间的 FK 合理化。
- TRUNCATE 语句
- 预计 TRUNCATE 会很快。
- TRUNCATE 不得有副作用。
- 如果用户无权删除表中的所有行,无论行级策略如何,则必须拒绝任何 TRUNCATE 操作。
- 将 TRUNCATE(一个 PostgreSQL 扩展,无论如何它不在 SQL 规范中)转换为 DELETE FROM 没有意义,并且多表 TRUNCATE 在 DELETE 中没有等效项。
- 因此,只需遵守 TRUNCATE,将 TRUNCATE 权限视为绕过 RLS 的能力。TRUNCATE 不会泄露数据,因此这是可行的。
- 表继承
- 只要它不会完全中断(或者我们确保它对继承中断),在初始版本中我们不必真正处理它
- 应在父项和子项关系中应用什么策略。
- 创意:还可以从父表复制行级策略。
路线图
当前路线图如下
RLS 直接在 9.3 中实现。由于需要修复有漏洞的视图问题,因此推迟了。
- 安全障碍视图 - 已完成,见 提交 0e4611c0234d89e288a53351f775c59522baed7c
- 防泄漏- 已完成,见 提交 cd30728fb2ed7c367d545fc14ab850b5fa2a4850
直接针对 9.4 的 RLS 实现由于需要减少与可更新视图的重叠,解决门户用户 ID 更改问题、修复外键强制,并且修复一些设计/抽象级别的问题而推迟。
- 实现 可自动更新的 security_barrier 视图(目标:9.4)。可能需要添加对以下内容的支持UPDATE, DELETE一个子查询,不展平。见 关于子查询的 UPDATE 或 DELETE。
- 修补门户实现(游标、引用游标等),以便用户 ID 和安全上下文随门户一起携带,在访问过程中进入/离开门户时保存/恢复。
- 在安全上下文更改后重新规划 RS 视图期间,支持重新运行查询重写器,以处理来自/到 RS 免除用户的更改。在用户 ID 更改或 RS 断言更改后,使涉及 RS 表的缓存计划失效。
- 添加行安全目录表、用于配置行安全的 SQL 语法
- 实现安全会话变量,记录它们用于行安全
- 实现强制访问控制和 SELinux 集成