SEPostgreSQL 开发
本维基页面的目的是向开发者介绍 SEPostgreSQL(安全增强 PostgreSQL)的设计规范。
SE-PostgreSQL 是一种安全选项,可通过 SQL 查询将 SELinux 的安全模型和安全策略应用于数据库对象。
以下链接有助于深入理解 SELinux。
- Fedora SELinux 用户指南
- 这是一份全面的 SELinux 文档,供最终用户使用,面向对 SELinux 一无所知的人们。
- SELinux 实例解析
- 这是一本介绍 SELinux 安全策略及其编写自己的安全策略模块方法的书。
简介
本章介绍 SELinux 中的安全模型,包括若干相应的术语和概念。
安全模型
从概念上讲,访问控制基于之前定义的规则,针对特定的主体(如数据库客户端)与特定的对象(如表)之间的允许(或拒绝)动作集合进行决策。我们可以将一个访问控制特性视为一个函数,该函数返回一个布尔值(允许或拒绝),用于给定的主体、对象和所需动作。但是,做出此访问控制决策的方式取决于其所基于的安全特性和安全模型的类型。
本机数据库权限机制采用 ACL(访问控制列表)模型。在此模型中,数据库对象存储数据库角色对和数据库对象上允许的动作对的列表。当用户的查询尝试访问特定的数据库对象时,将检查它。例如,我们可以在某个表上向某个用户授予SELECT权限。当用户尝试访问表时,本机数据库权限机制从表的 ACL 中查找合适的条目。如果匹配的条目允许所需的操作(SELECT在这个示例中),它允许用户的查询引用表。
在这种情况下,其做出决策的标准是数据库角色对和要访问的数据库对象的所需动作。其访问控制规则存储在数据库对象本身中,数据库所有者可以自行设置它们,因此此模型称为 DAC(自主访问控制)。
另一方面,SE-PostgreSQL 最重要的特性是强制任何数据库客户端均遵守由 SELinux 管理的正交访问控制规则。SELinux 是一项操作系统特性,并且包含一大套称为安全策略的访问控制规则(有关详细信息,请参见 安全策略)。它还包含用于控制数据库对象访问的规则,而不仅仅是内核对象。当一个数据库客户端尝试访问一个特定的数据库对象时,SE-PostgreSQL 需要做出其访问控制决策。此时,SE-PostgreSQL 会询问 SELinux 是否允许或不允许所需访问。接下来,SELinux 查找安全策略中的匹配条目,并回复一组在数据库客户端和所需的数据库对象之间的允许动作。
在本模型中,需要关注识别要检查的数据库客户端和数据库对象的方法。SELinux 的安全策略包含对各种对象(各种特性,例如有些对象没有名称,有些对象不是持久对象)的访问规则。SELinux 使用称为安全上下文的标识符将它们抽象出来,以一致的方式进行处理和描述(查看 安全上下文 以获取更多详细信息)。SELinux 中的访问控制规则被描述为针对一对安全上下文(一个是试图访问的主体,另一个是要访问的对象)的允许操作集。安全策略就是一大组这样的规则。
因此,当 SEPostgreSQL 需要 SELinux 的访问控制决策时,它会提供要访问的数据库客户端和数据库对象的安全上下文对。请注意这意味着需要检查的任何数据库对象都需要正确管理其安全上下文。
例如,当标签为system_u:system_r:httpd_t:s0(表示默认安全策略中的 Web 服务器进程)的数据库客户端尝试引用标记为system_u:object_r:sepgsql_ro_table_t:s0(表示“只读”表)的表时,使用SELECT语句,SEPostgreSQL 要求 SELinux 反馈与system_u:system_r:httpd_t:s0和system_u:object_r:sepgsql_ro_table_t:s0。然后,如果允许操作集包含db_table:{select}权限,它允许客户端访问该表。
在此情况中,做出决策的依据是安全上下文对。其访问控制规则由操作系统集中管理和维护,因而即使是资源所有者也无法设置这些规则。访问控制规则对任何用户强制执行,没有任何例外,因而该模型称为 MAC(强制访问控制)。
我们在 强制访问控制 中介绍了 MAC。查看该部分获取更多详细信息。
安全上下文
安全上下文是一个冒号分隔的短字符串,采用独立于对象类型的通用格式。第一个域表示 SELinux 用户,第二个表示角色,第三个表示类型(也称进程的域),第四个表示级别。
查看 Fedora SELinux 用户指南:第 3 章。SELinux 上下文 以获取更多详细信息。
$ ls -Z /etc/hosts -rw-r--r--. root root system_u:object_r:etc_t:s0 /etc/hosts
这显示了某个文件的安全上下文。
$ id -Z unconfined_u:unconfined_r:unconfined_t:s0
这显示了用户 shell 进程的安全上下文。
例如,当上述用户尝试读取/etc/hosts时,SELinux 会查找在unconfined_u:unconfined_r:unconfined_t:s0和和system_u:object_r:etc_t:s0file:{read}
,SELinux 将阻止它。权限,它允许客户端访问该表。请注意,分配给进程的安全环境的作用就像是一组特权一样。
如在 安全模型 中所述,SELinux 需要一对安全环境才能做出访问控制决策。它们分别是数据库客户端的安全环境和要访问的数据库对象的安全环境。
SELinux 提供了一个接口来检索连接到服务器进程的对等进程的安全环境。getpeercon(3)将返回给定套接字描述符的对等进程的安全环境。
int getpeercon(int sockfd, security_context_t *context);
SE-PostgreSQL 将从getpeercon()中检索到的安全环境作为数据库客户端的安全环境应用。请注意,它是垂直于数据库客户端登录后使用的数据库身份验证和数据库角色决定的。
另一方面,有必要为 SE-PostgreSQL 对其应用访问控制的每个数据库对象分配一个特定的安全环境。有关更多详细信息,请参阅 安全环境管理。
无论如何,数据库客户端和数据库对象都必须正确地标记为某个安全环境,以应用基于 SELinux 安全策略的访问控制规则。
安全策略
安全策略是 SELinux 中一组庞大的访问控制规则,而规则定义了一对安全环境(主题和对象)之间的一组允许操作。
安全策略中有多种类型的规则。以下示例是一个 TE(类型强制)规则,这是安全策略的主要部分。
allow httpd_t sepgsql_ro_table_t : db_table { getattr select lock };
此规则允许httpd_t域和sepgsql_ro_table_t类型之间有几个权限。标识符 (httpd_t和sepgsql_ro_table_t; 规则定义的左边) 是安全环境的第三字段。在默认安全策略中,Web 服务器进程执行标记为system_u:system_r:httpd_t:s0,而system_u:object_r:sepgsql_ro_table_t:s0是分配给表对象的候选安全环境。
当 Web 服务器进程尝试使用查询更新标记为system_u:object_r:sepgsql_ro_table_t的某个表时,SE-PostgreSQL 要求 SELinux 提供system_u:system_r:httpd_t:s0和system_u:object_r:sepgsql_ro_table_t:s0之间的一组允许操作。接下来,SELinux 查找其给定安全环境对的安全策略,然后匹配上述规则。此规则允许{ getattr select lock }对表对象进行权限,但其他权限(如{ update })并未明确允许。由于 SELinux 使用白名单方法,因此否定任何未明确允许的权限。
然后,SE-PostgreSQL 将检查允许的一组操作。在这种情况下,Web 服务器进程尝试更新标记为system_u:object_r:sepgsql_ro_table_t:s0的表,因此它需要db_table:{更新}权限,但它未包含在允许的操作集中。最后,将做出决策拒绝. SE-PostgreSQL 引发一个错误以阻止对表的可更新访问。
强制访问控制
本章介绍与强制访问控制相对应的规范。
通用原则
SE-PostgreSQL 的设计目标是应用 SELinux 的安全模型和安全策略,因此还继承了安全模型中的通用原则。
- 不可绕过的权限检查
原生数据库权限机制允许数据库超级用户绕过所有权限检查。这与root在操作系统中的想法类似。
但是,SELinux 不允许某个权限隐式覆盖其他权限。如果一个操作分别需要两种不同的权限,那么安全策略必须分别允许它们。
下面是一个示例
SELECT a, b FROM t WHERE c = 'aaa'
此查询尝试引用表t和列a, b和c作为WHERE子句的一部分。
原生数据库权限机制首先检查当前数据库角色是否具有数据库超级用户权限。如果数据库角色具有数据库超级用户权限,则应绕过其余权限检查。否则,它将检查SELECT表上的权限t。如果不允许,它还将检查SELECT所有引用的列上的权限。
SE-PostgreSQL 分别检查所有必需的权限。如果数据库客户端尝试以数据库超级用户的身份执行操作,它会检查db_database:{超级用户}对数据库的权限。然后,它还会检查db_table:{select}表上的权限t,和db_column:{选择}对列的权限a, b和c独立于db_database:{超级用户}和db_table:{select}.
上的访问控制决定。这反映了 SELinux 中的一项原则。它的安全模型根据用户与被访问对象之间的关系来决定用户是否可以在某个对象上执行某个操作。它永远不会被任何其他权限隐式覆盖,比如db_database:{超级用户}。此特性称为安全策略的可分析性。
- 独立于访问路径
下面是另一个原则。在 SELinux 的安全模型中,对某个对象检查的任何权限都独立于访问该对象的路径。
例如,如果/tmp/aaa是一个与/etc/hosts共享唯一文件实体的硬链接,我们可以使用两个路径来访问文件实体。但是 SELinux 总会做出与所用的访问路径无关的相同访问控制决定。
下面是数据库中的一个示例
CREATE VIEW v AS SELECT * FROM t WHERE a > 100
此视图提供了访问表t. 但 SE-PostgreSQL 要检查的任何权限与我们引用表格时的情况没有不同t不带视图,独立于访问路径。
(请注意,是否应为视图对象指定特征对象类以检查扩展视图的权限,这是一个单独的问题。)
这里有一个类似的示例
SELECT * FROM pg_namespace
PostgreSQL 允许通过常规 DML 语句引用系统目录。该db_schema应在pg_namespace对象类中给元组分配对象类。在这种情况下,由于行级访问控制,所有对数据库客户端不可见的元组都将从结果集中被过滤掉。这里,要检查的权限是db_schema:{getattr},而不是db_tuple:{select}.
(顺便说一下,由于安全原因,SE-PostgreSQL 不允许使用常规 DML 修改系统目录,有关更多详细信息,请参见 限制。)
通用对象行为
对象类是一个术语,表示某种类型的对象,例如文件、套接字、进程、数据库中的表格等。每个对象类都有自己的一组权限,这些权限反映了对象类的特征。然而,通常定义几个权限来处理通用对象行为;创建、删除、设置和获取属性,以及重新标记安全上下文(即设置属性的特例)。
此外,我们注意到处理与对象类无关的未标记对象的方式。
- 创建 (create)
在创建数据库对象时,SE-PostgreSQL 应在新的数据库对象上分配一个特定的安全上下文。如果给出了显式安全上下文(例如使用SECURITY_CONTEXT实例选项),SE-PostgreSQL 在新对象上分配它,只要它具有正确的格式。否则,SE-PostgreSQL 将分配一个基于安全策略计算的默认安全上下文。然后,SE-PostgreSQL 检查db_xxx:{create}使用默认或用户给定的安全上下文标记的新对象本身的权限。请注意,它无论如何都检查db_xxx:{create}权限与提供新对象的安全上下文的方式无关。
某些例程可以创建一个临时表格来实现ALTER TABLE它会更改列的定义,等等。在这种情况下,这不是用户可见的创建,因此 SE-PostgreSQL 不会对临时表的创建和删除应用任何权限检查。(但是,它检查db_column:{setattr}用户尝试更新属性的列上的权限。)
- 删除 (drop)
在删除数据库对象时,SE-PostgreSQL 应检查db_xxx:{drop}拒绝掉对象本身上的许可。当因级联中的删除操作而导致数据库对象被删除时也要检查。但有个例外是系统内部操作引起的删除,例如在会话关闭时清理临时对象、在删除表时移除元组等等。因为 SE-PostgreSQL 检查并控制用户的查询,但它不会阻止系统内部的操作。
- 获取属性 (getattr)
PostgreSQL 允许用户使用常规SELECT语句引用存储数据库对象属性的系统目录。在这种情况下,db_xxx:{getattr}许可应该作为行级访问控制的一部分来检查。注意db_tuple:{select}由于前面提到的原则,对于具有其自身对象类的数据库对象而言,不适用。
- 设置属性 (setattr)
当数据库对象的属性更新时,db_xxx:{setattr}应检查许可。通常,这个许可在ALTER语句中检查。注意CREATE OR REPLACE FUNCTION语句可以替换现有过程的定义。这种情况下,SE-PostgreSQL 应该检查db_procedure:{setattr}许可,即使给出了CREATE语句,因为它只更新现有的定义。一些ALTER语句同时需要多个许可。例如,ALTER TABLE ... ADD COLUMN ...语句更新表定义并创建新列,所以它需要同时检查db_table:{setattr}和db_column:{create}许可。
- 重新标记 (relabelfrom relabelto)
这是一类特殊的设置属性,出现在用户尝试更改数据库对象的安全性上下文时。在这种情况下,db_xxx:{relabelfrom}应在标记为较旧安全上下文的对象上检查许可,除了db_xxx:{setattr}许可。对标记为较新安全上下文的许可db_xxx:{relabelto}也应在对象上检查。
- 未标记对象
如果用户在禁用 SE-PostgreSQL 时创建新数据库对象,则不会对数据库对象分配安全性上下文。如果用户稍后启用了 SE-PostgreSQL,则在访问未标记的数据库对象时会应用一个伪安全性上下文。准确地说,如果 SE-PostgreSQL 检测到某个数据库对象具有无效或无安全性上下文,则它的处理方式如同已经分配给伪安全性上下文(称为未标记上下文)一样。在默认安全策略中,system_u:object_r:unlabeled_t:s0应被使用。
数据库对象
这db_database对象类应该分配给pg_database系统目录中的对象。它继承通用的对象行为和三种特征许可。
对于常见的对象行为没有特别指示。这些指示在CREATE DATABASE, ALTER DATABASE和DROP DATABASE语句中受检查,如果违反,则会引发一个错误。
- 默认安全上下文
默认的安全上下文db_database对象类应根据数据库客户端的安全上下文和数据库群集的根目录(标记为)来计算system_u:object_r:postgresql_db_t:s0常态下。
- 访问
这db_database:{access}权限应在服务器后端进程初始化期间数据库客户端尝试连接某数据库时受到检查。如果违反,SE-PostgreSQL 会引发一个错误。它可以阻止数据库客户端在没有此权限的情况下登录到某个数据库。
- load_module
不支持在 CommitFest-2009Sep。
这db_database:{load_module}权限应在由于用户查询而加载外部模块时受到检查;在外部模块中实现的调用过程或LOAD语句。此权限是在数据库和外部模块一对上定义的,所以不是数据库客户端。换句话说,它检查数据库加载某个外部模块的能力。如果违反,SE-PostgreSQL 会引发一个错误。
注意此权限不会在由于shared_preload_libraries而加载的模块上受到检查,因为它不受用户查询控制。但它也会检查local_preload_libraries,因为 libpq 协议允许在启动时建议后端,尽管它在严格意义上不是查询。
- superuser
这db_database:{超级用户}权限应在数据库客户端尝试作为可以绕过所有 DAC 权限检查的数据库超级用户执行时受到检查。如果违反,SE-PostgreSQL 会阻止它们作为数据库超级用户执行,所以客户端应作为非特权用户执行。注意此检查不会引发一个错误。
- SQL 增强
CREATE DATABASE name [ [ WITH ] [ OWNER [=] dbowner ] [ TEMPLATE [=] template ] [ ENCODING [=] encoding ] [ LC_COLLATE [=] lc_collate ] [ LC_CTYPE [=] lc_ctype ] [ TABLESPACE [=] tablespace ] [ CONNECTION LIMIT [=] connlimit ] ] [ SECURITY_CONTEXT [=] security_context ]
它允许创建一个标记为 security_context 的新数据库。
ALTER DATABASE name SECURITY_CONTEXT TO security_context
它允许将数据库的安全上下文重新标记为 security_context。
模式对象
这db_schema(或db_schema_temp用于临时名称空间)对象类应被分配在pg_namespace系统目录中的对象。它继承通用的对象行为和三种特征许可。
对于常见的对象行为没有特别指示。这些指示在CREATE SCHEMA, ALTER SCHEMA和DROP SCHEMA语句中受检查,如果违反,则会引发一个错误。
- 默认安全上下文
默认的安全上下文db_schema对象类应根据数据库客户端和存储新模式的数据库的安全上下文进行计算。
- 搜索
这db_schema:{search}权限应在用户查询试图在存储在模式对象中的任何数据库对象时受到检查。如果违反,此模式会从模式搜索路径中跳过,但 SE-PostgreSQL 不会引发一个错误。
- add_name
不支持在 CommitFest-2009Sep。
这db_schema:{add_name}当用户的查询试图在将要检查的模式中创建一个新的数据库对象、试图将数据库对象移动到将要检查的模式中或者试图重命名将要检查的模式中的数据库对象时,将检查权限。如果违规,SE-PostgreSQL 会引发一个错误。
- remove_name
不支持在 CommitFest-2009Sep。
这db_schema:{remove_name}当用户的查询试图删除将要检查的模式中的任何数据库对象、试图将数据库对象从将要检查的模式中移动或者试图重命名将要检查的模式中的数据库对象时,将检查权限。如果违规,SE-PostgreSQL 会引发一个错误。
这三个特色权限是文件系统上目录的类比,能够控制某个模式中所有数据库对象的创建、删除和使用。
- SQL 增强
CREATE SCHEMA schemaname [ AUTHORIZATION username ] [ SECURITY_CONTEXT security_context ] [ schema_element [ ... ] ] CREATE SCHEMA AUTHORIZATION username [ SECURITY_CONTEXT security_context ] [ schema_element [ ... ] ]
它能够创建标记为“security_context”的新模式。
ALTER SCHEMA name SECURITY_CONTEXT TO security_context
它能够将模式的安全上下文重新标记为“security_context”。
表对象
这db_table对象类应该分配给pg_class系统目录及其pg_class.relkind等于RELKIND_RELATION. 它继承通用对象行为以及六个特色权限。
通用对象权限检查与CREATE TABLE, ALTER TABLE和DROP TABLE语句同时进行,然后,如果违规,会引发一个错误。
需要注意的是,此操作可以同时访问多个对象。例如,当我们运行ALTER TABLE在表中添加新列时,它会创建一个新列对象,并更新表对象以递增pg_class.relnatts. 在这种情况下,db_table:{setattr}在该表中和db_column:{create}在新列中都需要得到允许。
- 默认安全上下文
默认的安全上下文db_table对象类别应根据数据库客户端的安全上下文以及存储新表的模式计算。
- select
这db_table:{select}当用户的查询试图使用SELECT, COPY TO以及任何其他类型的引用(例如WHERE和RETURNING子句搭配UPDATE语句)引用某些表时,将检查权限。如果违规,SE-PostgreSQL 会引发一个错误。
将要检查的目标表等同于本机数据库权限机制挑选出的表。SE-PostgreSQL 检查db_table:{select}对RangeTblEntry引用的所有表的权限,其中ACL_SELECT已针对requiredPerms设置。在COPY TO的情况下,目标表显而易见。
- update
这db_table:{更新}当用户的查询试图使用UPDATE更新某些表时,将检查权限。请注意,如果db_table:{select}也有UPDATE子句,则WHERE或RETURNING也是必需的。如果违规,SE-PostgreSQL 会引发一个错误。
将要检查的目标表等同于本机数据库权限机制挑选出的表。SE-PostgreSQL 检查db_table:{更新}对RangeTblEntry引用的所有表的权限,其中ACL_UPDATE已针对requiredPerms和引用的所有表的权限,其中modifiedColsACL_UPDATE别名为ACL_SELECT_FOR_UPDATE在SELECT ... FOR SHARE/UPDATE中设置,因此有必要在此有条件地分支,因为这两个语句之间的信息流向是相反的。
- 插入
这db_table:{insert}当用户尝试使用INSERT, COPY FROM和SELECT INTO更新某些表时,将检查权限。请注意,如果db_table:{select}也有INSERT子句,则RETURNING也是必需的。如果违规,SE-PostgreSQL 会引发一个错误。
将要检查的目标表等同于本机数据库权限机制挑选出的表。SE-PostgreSQL 检查db_table:{insert}对RangeTblEntry引用的所有表的权限,其中ACL_INSERT引用的所有表的权限进行检查,目标表很明显。请注意,本地数据库特权机制不会检查requiredPerms设置。在COPY TO和SELECT INTO中新创建表的权限INSERT因为这显然是被允许的。但是,SE-PostgreSQL 需要对此进行检查,因为安全策略可能会阻止向被标签为默认安全上下文的表中插入数据。(这可能是疯狂的安全策略,但 SE-PostgreSQL 需要遵循该决定。)SELECT INTO删除
- db_table:{delete}
这当用户尝试使用DELETETRUNCATE或从特定表中删除数据时,应检查权限更新某些表时,将检查权限。请注意,如果db_table:{select}也有TRUNCATE子句,则WHERE或RETURNING也是必需的。如果违规,SE-PostgreSQL 会引发一个错误。
将要检查的目标表等同于本机数据库权限机制挑选出的表。SE-PostgreSQL 检查当用户尝试使用对RangeTblEntry引用的所有表的权限,其中ACL_DELETE引用的所有表的权限进行检查,目标表很明显。请注意,本地数据库特权机制不会检查requiredPerms设置。在从特定表中删除数据时,应检查权限,目标表很明显。SE-PostgreSQL 认为从特定表中删除数据时,应检查权限等同于无条件删除。因此,它还会检查行级访问控制中是否无无法删除的元组。
- 锁定
这db_table:{lock}当用户尝试使用LOCK或SELECT ... FOR SHARE/UPDATE获取显式表锁时,应检查权限。请注意,它不会检查常规操作期间获取的隐式表锁。
- 引用
这db_table:{reference}当用户尝试在表上设置 FK 约束时,应检查权限。如果违反,SE-PostgreSQL 将引发错误。
- SQL 增强
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name ( [ { column_name data_type [ DEFAULT default_expr ] [ column_constraint [ ... ] ] [ SECURITY_CONTEXT column_context ] | table_constraint | LIKE parent_table [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ] ... } [, ... ] ] ) [ INHERITS ( parent_table [, ... ] ) ] [ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ] [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] [ TABLESPACE tablespace ] [ SECURITY_CONTEXT table_context ]
它可以创建一个标记为 security_context 的新表。
ALTER TABLE name SECURITY_CONTEXT TO security_context ALTER TABLE name ALTER [ COLUMN ] column SECURITY_CONTEXT TO security_context
它可以将表的 security context 重新标记为 security_context。
Sequence 对象
这db_sequence对象类应该分配给pg_class系统目录及其pg_class.relkind等于RELKIND_SEQUENCE。它继承了公共对象行为以及三个特征权限。
对于常见的对象行为没有特别指示。这些指示在CREATE SEQUENCE, ALTER SEQUENCE和DROP SEQUENCE语句中受检查,如果违反,则会引发一个错误。
- 默认安全上下文
默认的安全上下文db_sequence应基于数据库客户端和存储新序列的架构的 security context 计算对象类。
- 获取值
这db_sequence:{get_value}当用户尝试使用currval(), lastval()或SELECT语句)引用某些表时,将检查权限。如果违规,SE-PostgreSQL 会引发一个错误。
- next_value
这db_sequence:{next_value}引用特定序列对象时,应检查权限,当用户尝试使用nextval()从特定序列对象中获取值时。如果违反,SE-PostgreSQL 将引发错误。
- 设置值
这db_sequence:{set_value}当用户尝试使用setval()从特定序列对象中获取值时。如果违反,SE-PostgreSQL 将引发错误。
- SQL 增强
CREATE [ TEMPORARY | TEMP ] SEQUENCE name [ INCREMENT [ BY ] increment ] [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ] [ START [ WITH ] start ] [ CACHE cache ] [ [ NO ] CYCLE ] [ OWNED BY { table.column | NONE } ] [ SECURITY_CONTEXT security_context ]
它允许创建一个标记为security_context的新序列。
ALTER SEQUENCE name SECURITY_CONTEXT TO security_context
它允许将序列的安全上下文重新标记为security_context。
过程对象
这db_procedure对象类应该分配给pg_proc系统目录中的对象。它继承通用的对象行为和三种特征许可。
对于常见的对象行为没有特别指示。这些指示在CREATE FUNCTION, ALTER FUNCTION和DROP FUNCTION语句中受检查,如果违反,则会引发一个错误。
- 默认安全上下文
默认的安全上下文db_procedure对象类将根据数据库客户端和存储新过程的模式的安全上下文进行计算。
- execute
这db_procedure:{execute}在用户查询尝试执行包含操作符实现的过程时,应当检查权限。
目标过程的检查形式大多等同于pg_proc_aclcheck(..., ACL_EXECUTE),但两者在几个点上并不完全相同。ACL_EXECUTE经常在用户定义新数据库对象(在内部使用特定函数,例如DefineOperator(), CreateConversionCommand()等)时进行检查。
这db_procedure:{execute}权限在特定函数与实际调用该函数的数据库客户端之间定义,但这并不意味着允许每个人都将函数作为系统内部的一部分进行调用,比如转换。在这种情况下,db_procedure:{install}应当被用作替代方案。
此外,has_function_privilege()也将检查ACL_EXECUTE,但db_procedure:{execute}在此不应被检查。
- entrypoint
这db_procedure:{entrypoint}在用户查询尝试调用受信任的过程的时候,应当检查权限。注意db_procedure:{execute}对于调用受信任的过程也是必需的。
请参阅受信任的过程了解更多详细信息。
- install
不支持在 CommitFest-2009Sep。
这db_procedure:{install}在用户查询尝试定义一个可以在内部调用用户定义函数的数据库对象时,应当检查权限,没有db_procedure:{execute}关于运行时的检查。
此权限检查能最小化将恶意函数安装为系统内部内容的一部分的风险,然后有人隐式地调用它们。
特别是,应当检查这些函数类型处理程序、强制转换函数、转换函数、外部数据包装器验证器、触发器函数和全文搜索解析器/模板函数。
- untrusted
这db_procedure:{untrusted}在用户查询尝试使用不受信任的语言(如 C)创建或替换某个函数时,应当检查权限,除了db_procedure:{create}或db_procedure:{setattr}.
因为 SE-PostgreSQL 无法防止系统内部访问,所以有必要最小化定义恶意函数的风险。
- SQL 增强
CREATE [ OR REPLACE ] FUNCTION name ( [ [ argmode ] [ argname ] argtype [ { DEFAULT | = } defexpr ] [, ...] ] ) [ RETURNS rettype | RETURNS TABLE ( colname coltype [, ...] ) ] { LANGUAGE langname | WINDOW | IMMUTABLE | STABLE | VOLATILE | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | COST execution_cost | ROWS result_rows | SET configuration_parameter { TO value | = value | FROM CURRENT } | AS 'definition' | AS 'obj_file', 'link_symbol' | SECURITY_CONTEXT security_context } ... [ WITH ( attribute [, ...] ) ]
它允许创建一个标记为security_context的新过程。
ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) SECURITY_CONTEXT TO security_context
它允许将过程的安全上下文重新标记为security_context。
列对象
这db_column对象类应该分配给pg_attribute系统目录及其attrelid指向某个 Table 对象。它继承了普通对象行为以及四项特征性权限。请注意db_column:{delete}未定义权限。
通用对象权限检查与CREATE TABLE, ALTER TABLE和DROP TABLE,然后如果遭到违反,它会引发错误。
在大多数情况下,列对象将与包含它们的表同时创建和舍弃,因此表上的权限也必须得到允许,而不仅仅是列权限。ALTER TABLE具有创建和舍弃列的多种选项,而不仅是设置列的属性。在这种情况下,db_column:{create}和db_column:{drop}应该检查虽然ALTER使用了语句。
- 默认安全上下文
默认的安全上下文db_column基于数据库客户端的安全上下文以及包含新列的表的安全上下文计算对象类。
- select
这db_column:{选择}在用户的查询尝试使用SELECT, COPY TO以及任何其他类型的引用(例如WHERE或RETURNING子句搭配UPDATE语句)引用某些表时,将检查权限。如果违规,SE-PostgreSQL 会引发一个错误。
引用某些列时,应检查列的权限。要检查的目标列与本地数据库权限机制获取的列相同,但即使db_table:{select}也会进行检查。SE-PostgreSQL 检查db_column:{选择}中标记的所有列的权限selectedCols的RangeTblEntry引用的所有表的权限,其中ACL_SELECT引用的所有表的权限进行检查,目标表很明显。请注意,本地数据库特权机制不会检查requiredPerms。对于 COPY TO,目标列很明显。
- update
这db_column:{update}在用户的查询使用UPDATE从特定序列对象中获取值时。如果违反,SE-PostgreSQL 将引发错误。
引用某些列时,应检查列的权限。要检查的目标列与本地数据库权限机制获取的列相同,但即使db_table:{更新}也会进行检查。SE-PostgreSQL 检查db_column:{update}中标记的所有列的权限引用的所有表的权限,其中的RangeTblEntry引用的所有表的权限,其中ACL_UPDATE引用的所有表的权限进行检查,目标表很明显。请注意,本地数据库特权机制不会检查requiredPerms.
- 插入
这db_column:{insert}在用户的查询使用INSERT, COPY TO和SELECT INTO从特定序列对象中获取值时。如果违反,SE-PostgreSQL 将引发错误。
引用某些列时,应检查列的权限。要检查的目标列与本地数据库权限机制获取的列相同,但即使db_table:{insert}也会进行检查。SE-PostgreSQL 检查db_column:{insert}中标记的所有列的权限引用的所有表的权限,其中的RangeTblEntry引用的所有表的权限,其中ACL_INSERT引用的所有表的权限进行检查,目标表很明显。请注意,本地数据库特权机制不会检查requiredPerms.
- 引用
这db_column:{reference}更新某些列时,应检查列的权限在用户的查询尝试在列上设置 FK 约束时。如果遭到违反,SE-PostgreSQL 会引发错误。
- SQL 增强
请参阅 Table 对象 以获取详细信息。
Tuple 对象
这db_tuple对象类应分配给存储在常规表中的任何对象,但不拥有其特征性对象类。它继承了普通对象行为,但其权限名除外,且没有特征性权限。
- select
这db_tuple:{select}在用户的查询尝试使用SELECT, COPY TO以及任何其他类型的引用(例如WHERE或RETURNING子句搭配UPDATE语句引用时,应检查权限。这相当于db_xxx:{getattr}中其他对象类的
- update
这db_tuple:{update}在用户的查询尝试使用UPDATE更新时,应检查权限。这相当于db_xxx:{setattr}中其他对象类的
- 插入
这db_tuple:{insert}在用户的查询尝试使用INSERT, SELECT INTO或COPY FROM更新时,应检查权限。这相当于db_xxx:{create}在其他对象类中插入时,应检查权限。如果没有提供显式安全上下文,则应向新的元组分配一个默认安全上下文。然后,在标记的元组上进行检查。
- db_table:{delete}
这db_tuple:{delete}在用户的查询尝试使用TRUNCATE或从特定表中删除数据时,应检查权限更新时,应检查权限。这相当于db_xxx:{drop}中其他对象类的
- relabelfrom
删除时,应检查权限。这相当于db_xxx:{relabelfrom}中其他对象类的
- relabelto
删除时,应检查权限。这相当于db_xxx:{relabelto}中其他对象类的
表空间对象
这db_tablespace对象类应该分配给pg_tablespace系统目录。
它继承了普通对象行为以及一项特征性权限。对于普通对象行为没有特殊说明。这些将在 CREATE TABLESPACE、ALTER TABLESPACE 和 DROP TABLESPACE 语句中得到检查,然后如果遭到违反,就会引发错误。
- 默认安全上下文
默认的安全上下文db_tablespace对象类应根据数据库客户端的安全上下文和数据库群集的根目录(标记为)来计算system_u:object_r:postgresql_db_t:s0常态下。
- createon
这db_tablespace:{createon}当用户的查询尝试使用除默认表空间之外的特定表空间时,应检查权限。它等同于ACL_CREATE在原生数据库权限机制上。
- SQL 增强
CREATE TABLESPACE tablespacename [ OWNER username ] [ SECURITY_CONTEXT security_context ] LOCATION 'directory'
ALTER TABLESPACE name SECURITY_CONTEXT TO security_context
大对象
这db_blob对象类应分配给由存储在pg_largeobject系统目录中的多个页面帧组成的大对象。它继承了常见对象行为以及四个特性权限。
- 默认安全上下文
默认的安全上下文db_blob应基于数据库客户端和包含新建大对象的数据库的安全上下文计算对象类。
- 读取
这db_blob:{read}当用户查询尝试使用loread()或lo_export().
- 读取大对象内容时,应检查权限。
这写入db_blob:{write}当用户查询尝试使用, lowrite()或lo_truncate().
- lo_import()
这写入大对象内容时,应检查权限。导入写入和file:{read}
,SELinux 将阻止它。db_blob:{import}- 当用户查询尝试将特定文件系统对象导入到新创建的大对象时,应检查权限。请注意
这还应检查源文件上的权限。导出db_blob:{read}和db_blob:{export}当用户查询尝试将大对象导出到特定文件系统对象时,应检查权限。请注意
还应检查目标文件上的
file:{write}
- 读取
这file:{read}
,SELinux 将阻止它。权限。文件系统交互PostgreSQL 允许用户使用少数几个界面访问服务器端文件系统。, lo_truncate()和当用户查询尝试读取服务器端文件系统上的特定文件时,应检查权限。请注意,此检查在数据库客户端的安全上下文和目标文件安全上下文之间执行。有必要通过 PostgreSQL 服务器进程控制目标文件到数据库客户端的信息流。.
- 读取大对象内容时,应检查权限。
这db_blob:{export}具体来说,会在以下事件上检查此权限
文件系统交互COPY FROM filenamepg_read_file()
当用户查询尝试在服务器端文件系统上写入某个特定文件时,应检查权限。请注意,此检查在数据库客户端的安全上下文和目标文件安全上下文之间执行。有必要通过 PostgreSQL 服务器进程控制从数据库客户端到目标文件的信息流。
COPY TO filename
以及 lo_export() 函数。
安全上下文管理
这SE-PostgreSQL 将在数据库对象上分配安全上下文,并对其进行正确管理。此功能与制定访问控制决策一样重要,因为它是一切强制访问控制的准则。security_context 系统列security_context
系统列用于导入和导出数据库对象的安全性上下文。它声明为
postgres=# SELECT security_context, relname, relkind FROM pg_class WHERE oid = 'drink'::regclass; security_context | relname | relkind ------------------------------------------+---------+--------- unconfined_u:object_r:sepgsql_table_t:s0 | drink | r (1 row)
这SE-PostgreSQL 将在数据库对象上分配安全上下文,并对其进行正确管理。系统列可以用来给新插入的元组提供一个明确的安全上下文。
postgres=# INSERT INTO drink (security_context, id, name, price) \ VALUES ('unconfined_u:object_r:sepgsql_table_t:s0:c0', 1, 'green tea', 150); INSERT 16440 1 postgres=# SELECT security_context, * FROM drink; security_context | id | name | price ---------------------------------------------+----+-----------+------- unconfined_u:object_r:sepgsql_table_t:s0:c0 | 1 | green tea | 150 (1 row)
这SE-PostgreSQL 将在数据库对象上分配安全上下文,并对其进行正确管理。只要系统列有正确的安全上下文格式,就可以接受任意文本输入。
postgres=# UPDATE drink SET security_context = 'unconfined_u:object_r:sepgsql_table_t:s0:c' || id; UPDATE 1 postgres=# SELECT security_context, * FROM drink; security_context | id | name | price ---------------------------------------------+----+-----------+------- unconfined_u:object_r:sepgsql_table_t:s0:c1 | 1 | green tea | 150 (1 row) postgres=# UPDATE drink SET security_context = 'this is invalid security context'; ERROR: Invalid security context: "this is invalid security context"
如果目标列表SELECT INTO包含一个属性名称SE-PostgreSQL 将在数据库对象上分配安全上下文,并对其进行正确管理。,则其值将用于给新插入的元组提供一个明确的安全上下文。只要有正确的格式,它也接受security_context 系统列类型值。
postgres=# SELECT 'unconfined_u:object_r:sepgsql_ro_table_t:s0:c' || id AS security_context, * INTO t FROM drink; SELECT postgres=# SELECT security_context, * FROM t; security_context | id | name | price ------------------------------------------------+----+-----------+------- unconfined_u:object_r:sepgsql_ro_table_t:s0:c1 | 1 | green tea | 100 unconfined_u:object_r:sepgsql_ro_table_t:s0:c2 | 2 | coke | 120 unconfined_u:object_r:sepgsql_ro_table_t:s0:c3 | 3 | juice | 130 unconfined_u:object_r:sepgsql_ro_table_t:s0:c4 | 4 | cofee | 180 (4 rows)
pg_security 系统编目
安全上下文的特点之一是,大量的对象共享有限数量的安全上下文。SELinux 对标有相同安全上下文的不同对象的访问操作始终做出相同的访问控制决策。这意味着从访问控制的角度来看,它们之间没有差别。
典型的安全上下文是一个大约 40 至 60 字节长度的文本。如果我们为每个数据库对象(包括用户元组)以文本形式存储这些元组,这将成为徒劳地占用存储空间的一个因素。
这pg_security系统编目支持存储文本形式的安全上下文,而不是其他表。
以下是pg_security.
#define SecurityRelationId 3400 CATALOG(pg_security,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS { /* Identifier of the security attribute */ Oid secid; /* OID of the database which refers the entry */ Oid datid; /* OID of the table which refers the entry */ Oid relid; /* Text representation of security attribute */ text secattr; } FormData_pg_security;
的定义,任何数据库对象都可以有一个安全标识符(Oid; 4 字节长度),而不是文本形式的安全上下文。它存储在HeapTupleHeader结构的可变长度字段中,就好像oid在执行中类似。
struct HeapTupleHeaderData +---------------------------------+ +0 | union { | | HeapTupleFields t_heap; | | DatumTupleFields t_datum;| | } t_choice; | +---------------------------------+ +12 | ItemPointerData t_ctid; | +---------------------------------+ +18 | uint16 t_infomask2; | +---------------------------------+ +20 | uint16 t_infomask; | +---------------------------------+ +22 | uint8 t_hoff; o--------------------+ +---------------------------------+ +23 | | NULL bitmaps | | : : | | | | +---------------------------------+ +(t_hoff - 8) | | Oid security identifier | | +---------------------------------+ +(t_hoff - 4) | | Oid object identifier | | +---------------------------------+ +(t_hoff) <-----+ | Data contents | : :
数据库对象的安全性标识符指定pg_security.secid。文本形式的安全上下文与secid, datid(包含引用安全上下文的数据库对象所在的数据库的 OID)以及relid(包含引用安全上下文的关联关系的 OID)的组合相关联。datid和relid用于缩小引用特定安全上下文的数据库对象的范围,以在它变为未引用时回收它。
此功能不仅可以减少存储消耗,还可以快速做出访问控制决策。
访问矢量缓存(AVC;见 访问矢量缓存)使正在用户空间中使用的访问控制决策能够被缓存。如果安全上下文被表示为安全标识符,SE-PostgreSQL 可以使用整数比较(不是文本比较)查询缓存的条目。从以前的衡量来看,它对性能有不可忽视的影响。
- 新的安全上下文
如果用户给出了在pg_security上未找到的新安全上下文INSERT, CREATE或ALTER语句,后端将在pg_security中追加一个新条目,并为已插入或已更新的数据库对象分配一个新的安全标识符。
从用户的角度来看,它被处理为security_context 系统列数据被接受。
- 回收孤立条目
SE-PostgreSQL 提供了一个用于回收孤立安全上下文的函数。
如果关系中未引用某一特定安全上下文条目,由relid所识别的数据库标识datid,我们可以删除该条目。
postgres=# select security_reclaim_context('t1'); security_reclaim_context -------------------------- 6 (1 row)
这等同于以下查询,但用户无法在不使用某一特定功能的情况下直接修改pg_security。
LOCK tablename IN SHARE MODE DELETE FROM pg_security WHERE datid = MyDatabaseId AND relid = tablename::regclass AND secid NOT IN (SELECT security_context_to_secid(tablename) FROM tablename)
Mctrans 支持
安全上下文具有两种形式。一种是原始格式,用于与内核中 SELinux 通信。另一种是转换格式,用于为用户打印安全上下文和接受用户给定的输入。
原始格式的示例
system_u:object_r:sepgsql_table_t:s0:c0
转换格式的示例
system_u:object_r:sepgsql_table_t:Classified
安全上下文的右侧字段称为范围。它是机密性(如s0)和类别(如c0)的配对。如果mcstrans守护程序在系统中可用,它可以提供一种格式之间互相转换的工具。
范围字段用于实施传统多级安全策略,因此,它还用于使用标记联网特性,用支持 mac 的任何其他专有 unix 系统进行通信。因此,在导入或导出安全上下文时,必须转换范围字段的格式。
当mcstrans守护程序在系统中可用时,SE-PostgreSQL 能够在转换格式下接收安全上下文,并能够在转换格式下打印安全上下文。然而,它总是以原始格式存储安全上下文,因为只有在mcstrans可用时,转换格式才可用,而内核无法接收它。
其他特性
行级访问控制
行级访问控制执行的功能,就好比在扫描目标表时,会丢弃任何违规元组的过滤器。
请参见以下示例
postgres=# SELECT security_context, * FROM drink; security_context | id | name | price ------------------------------------------------+----+-------+------- system_u:object_r:sepgsql_table_t:Unclassified | 1 | water | 100 system_u:object_r:sepgsql_table_t:Unclassified | 2 | coke | 120 system_u:object_r:sepgsql_table_t:Classified | 3 | beer | 240 system_u:object_r:sepgsql_table_t:Classified | 4 | wine | 380 (4 rows)
如果数据库客户端可以看到标有未分类和已分类的元组,它会返回四个元组。
另一方面,如果数据库客户端只能看到未分类,则标有已分类的任何元组都会从结果集中被过滤掉。
postgres=# SELECT security_context, * FROM drink; security_context | id | name | price ------------------------------------------------+----+-------+------- system_u:object_r:sepgsql_table_t:Unclassified | 1 | water | 100 system_u:object_r:sepgsql_table_t:Unclassified | 2 | coke | 120 (2 rows)
它也适用于UPDATE和TRUNCATE语句,在扫描目标表时,不仅扫描SELECT语句。SE-PostgreSQL 在使用WHERE子句评估用户给定的条件之前对取回的元组进行访问控制决策,因为用户可以给出恶意函数,会向他人泄露其参数。(请注意,我们假设索引访问方法是受信任的,因此行级访问控制在索引访问后应用这件事并不重要。)
这INSERT语句不会扫描目标表,因此不能将其实现为一个过滤器。如果用户尝试使用插入一个带有某一特定安全上下文的元组,SE-PostgreSQL 会在违规时产生一个错误。
当用户尝试使用UPDATE语句,它还将针对以下违规情况引发错误db_tuple:{relabelfrom relabelto}权限。因为有必要在所有行前触发器之后检查权限,因此 SE-PostgreSQL 无法在扫描时做出决定。
- 异常
外键约束实现为触发器函数,它启动辅助查询来检查和保持参照完整性。在这种情况下,最好不要滤出不可见元组,因为它可能引用用户尝试更新或删除的元组。因此,SE-PostgreSQL 在外键约束检查期间在行级访问控制中切换行为。在这种情况下,它将在访问违规时引发错误。请注意,有必要在所有给定的条件检查后做出访问控制决定,因为它可能由于不相关的元组而引发错误。此处,假设系统内部内容不会在WHERE子句的一部分。
受信任过程
受信任过程的概念可以切换数据库客户端的安全上下文,就好像数据库特权机制提供安全定义器函数一样。
安全定义器函数在执行函数期间转换有效的数据库角色,并且这会影响数据库特权机制的访问控制决策。类似地,受信任过程在执行函数期间转换数据库客户端的安全上下文。请注意,数据库客户端的安全上下文等同于强制访问控制的一组特权。
在默认安全策略中,sepgsql_trusted_proc_exec_t是唯一以前定义的作为受信任过程的类型。它可以提供一种安全的方法来访问不可见或只读数据。
例如,当customer表定义如下时,无特权用户无法看到customer.credit的内容,因为它标记为sepgsql_secret_table_t这意味着所有访问都从受限域中拒绝。
CREATE TABLE customer ( cid integer primary key, cname varchar(32), credit varchar(32) SECURITY_CONTEXT = 'system_u:object_r:sepgsql_secret_table_t:s0' );
因为customer.credit存储客户的信用卡号,我们不希望向 Web 应用程序公开它们。但是,在线购物应用程序需要向用户显示一部分编号以进行确认。在这种情况下,我们可以为该目的声明一个受信任过程,如下所示
CREATE OR REPLACE FUNCTION show_credit (integer) RETURNS text LANGUAGE 'sql' SECURITY_CONTEXT = 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0' AS 'SELECT regexp_replace(credit, -[0-9]+, -xxxx, g) FROM customer WHERE cid = $1';
它执行如下
postgres=# SELECT cid, cname FROM customer; cid | cname -----+------- 10 | jack 13 | adam 14 | liza (3 rows) postgres=# SELECT cid, cname, credit FROM customer; ERROR: SELinux: security policy violation
列级访问控制阻止受限数据库客户端访问customer.credit。但是,在show_credit()执行期间,数据库客户端的安全上下文会暂时切换,它会部分隐藏地返回机密数据。
postgres=# SELECT cid, cname, show_credit(cid) FROM customer; cid | cname | show_credit -----+-------+--------------------- 10 | jack | 1111-xxxx-xxxx-xxxx 13 | adam | 5555-xxxx-xxxx-xxxx 14 | liza | 9876-xxxx-xxxx-xxxx (3 rows)
审核日志
当 SE-PostgreSQL 由于访问违规而阻止用户查询时,它会生成如下审核日志
LOG: SELinux: denied { select } scontext=unconfined_u:unconfined_r:httpd_t:s0 \ tcontext=system_u:object_r:sepgsql_secret_table_t:s0 \ tclass=db_column name=customer.credit STATEMENT: select * from customer;
它记录了unconfined_u:unconfined_r:httpd_t:s0尝试引用customer.credit标记有system_u:object_r:sepgsql_secret_table_t:s0的列,但失败。
安全策略提供了两种规则来控制审核日志的生成。一种是auditdeny用于生成访问拒绝事件的审计日志的规则,另一个是auditallow用于生成允许访问事件的审计日志的规则。默认情况下,记录所有拒绝访问的事件,不记录其他事件。
此外,默认安全策略还关闭了db_tuple对象类的拒绝访问日志以禁止大量审计日志,因为用户的查询一次可以访问大量的元组。
访问向量缓存
当 SE-PostgreSQL 与内核中的 SELinux 通信时,由于系统调用调用,它需要进行上下文切换。然而,这通常是一个繁重的操作,因此有必要通过减少系统调用调用次数来最大程度减少对性能的不利影响。特别是,一个查询可以同时获取大量的元组,因此快速做出决策至关重要。
AVC(访问向量缓存)的思想是缓存最近使用的访问控制决策。在大多数情况下,大量的对象倾向于共享有限数量的安全上下文,并且 SELinux 为一对相同安全上下文返回相同的结果。因此,我们可以预期大部分访问控制决策不需要内核调用。(根据经验,99.99% 以上的访问控制决策都会命中 AVC。)
当客户端发出查询时,SE-PostgreSQL 需要检查其对目标数据库对象的权限。首先,它在给定的安全上下文对中查找 AVC。如果找到,它可以立即做出访问控制决定。否则,它将根据通信协议调用内核中的 SELinux。
AVC 还具有良好的特性。它可以利用安全标识符查找缓存表,而无需进行任何安全上下文中的字符串比较。这意味着我们不必在 SE-PostgreSQL 做出决策时将安全标识符转换为相应的文本形式的安全上下文。
限制
请注意在使用 SE-PostgreSQL 时存在一些限制。
它对访问系统目录和 toast 关系施加了一些硬连线规则。它阻止使用通用 DML 语句(INSERT, UPDATE和TRUNCATE)修改系统目录,因此用户需要改为使用常规 DDL 语句。它还阻止使用通用 DML 语句(包括SELECT)访问 toast 关系,因此只有内部内容可以访问它们。
出于安全原因,它需要禁用几种类型的优化。使用 SQL 语言声明的 SQL 函数可以由优化器展平和内联,但如果函数被标记为受信任的过程,SE-PostgreSQL 将限制这种类型的优化。
(TODO在此处放置需要禁用优化的任何其他案例)
行级别访问控制与唯一或外键约束之间的交互可能暗示隐形元组的存在,尽管用户无法查看数据本身。例如,当我们尝试在隐形元组中插入具有重复键值的元组时,它将引发错误,以便我们可以知道隐形元组的存在。这种不规则的信息流称为隐蔽通道。通用标准中也提到了这一点,但除非使用极端环境,否则不为必须。请注意,SE-PostgreSQL 并不关心消除隐蔽通道。