2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4

39.5. 规则和权限 #

由于 PostgreSQL 规则系统重写查询,会访问除原始查询中使用的表/视图之外的其他表/视图。当使用更新规则时,这可能包括对表的写入访问。

重写规则没有单独的所有者。关系(表或视图)的所有者就是为它定义的重写规则的自动所有者。 PostgreSQL 规则系统会更改默认访问控制系统的行为。除了与安全调用者视图关联的 SELECT 规则(请参阅 CREATE VIEW)之外,所有由于规则而使用的关系都会根据规则所有者的权限进行检查,而不是根据调用规则的用户的权限进行检查。这意味着,除了安全调用者视图之外,用户只需要对其查询中显式命名的表/视图具有必要的权限。

例如:一个用户有一个电话号码列表,其中一些是私有的,另一些则与办公室助理有关。该用户可以构建以下内容

CREATE TABLE phone_data (person text, phone text, private boolean);
CREATE VIEW phone_number AS
    SELECT person, CASE WHEN NOT private THEN phone END AS phone
    FROM phone_data;
GRANT SELECT ON phone_number TO assistant;

除了该用户(和数据库超级用户)之外,任何人都无法访问 phone_data 表。但由于 GRANT,助理可以对 phone_number 视图运行 SELECT。规则系统会将 SELECTphone_number 重写为从 phone_data 进行 SELECT。由于该用户是 phone_number 的所有者,因此也是该规则的所有者,对 phone_data 的读取访问将根据该用户的权限进行检查,查询将被允许。还会执行访问 phone_number 的检查,但这会针对调用者进行,因此除了该用户和助理之外,没有人可以使用它。

权限是逐条规则检查的。因此,目前只有助理可以看到公共电话号码。但助理可以设置另一个视图并将访问权限授予公众。然后,任何人都可以通过助理的视图查看 phone_number 数据。助理无法做的是创建一个直接访问 phone_data 的视图。(实际上助理可以,但不会起作用,因为在权限检查期间所有访问都会被拒绝。)一旦该用户发现助理打开了他们的 phone_number 视图,该用户就可以撤销助理的访问权限。这样,对助理视图的任何访问都会失败。

有人可能会认为这种逐条规则检查是一个安全漏洞,但实际上并非如此。但是,如果不是这样工作的,助理可以设置一个具有与 phone_number 相同列的表,并每天将数据复制到其中。然后,这将是助理自己的数据,助理可以授予任何人他们想要的访问权限。 GRANT 命令意味着“我信任你”。如果你信任的人做了上述事情,就该重新考虑一下,然后使用 REVOKE

请注意,虽然可以使用上述技术创建视图来隐藏某些列的内容,但除非设置了 security_barrier 标志,否则它们无法可靠地隐藏不可见行中的数据。例如,以下视图是不安全的

CREATE VIEW phone_number AS
    SELECT person, phone FROM phone_data WHERE phone NOT LIKE '412%';

该视图可能看起来是安全的,因为规则系统会将任何从 phone_number 进行的 SELECT 重写为从 phone_data 进行的 SELECT,并添加仅选择 phone 不以 412 开头的条目的限定条件。但是,如果用户可以创建自己的函数,那么说服规划器在 NOT LIKE 表达式之前执行用户定义的函数并不难。例如

CREATE FUNCTION tricky(text, text) RETURNS bool AS $$
BEGIN
    RAISE NOTICE '% => %', $1, $2;
    RETURN true;
END;
$$ LANGUAGE plpgsql COST 0.0000000000000000000001;

SELECT * FROM phone_number WHERE tricky(person, phone);

将打印 phone_data 表中的每个人和电话号码作为 NOTICE,因为规划器会选择先执行廉价的 tricky 函数,而不是更昂贵的 NOT LIKE。即使用户被阻止定义新函数,内置函数也可以用于类似的攻击。(例如,大多数类型转换函数在其生成的错误消息中都包含输入值。)

类似的考虑也适用于更新规则。在前一节的示例中,示例数据库中表的拥有者可以授予某人对 shoelace 视图的 SELECTINSERTUPDATEDELETE 权限,但只能授予对 shoelace_logSELECT 权限。写入日志条目的规则操作仍然会成功执行,该用户可以看到日志条目。但他们无法创建假条目,也无法操纵或删除现有条目。在这种情况下,通过说服规划器更改操作顺序来颠覆规则是不可能的,因为唯一引用 shoelace_log 的规则是未限定的 INSERT。在更复杂的场景中可能并非如此。

当视图需要提供行级安全时,应将 security_barrier 属性应用于该视图。这可以防止恶意选择的函数和运算符在视图完成其工作之后才接收来自行的值。例如,如果上述视图已这样创建,它将是安全的

CREATE VIEW phone_number WITH (security_barrier) AS
    SELECT person, phone FROM phone_data WHERE phone NOT LIKE '412%';

使用 security_barrier 创建的视图的性能可能比未使用此选项创建的视图差很多。通常,没有办法避免这种情况:如果最快的计划可能会损害安全,就必须拒绝它。因此,此选项默认不启用。

查询规划器在处理没有副作用的函数时具有更大的灵活性。这类函数被称为 LEAKPROOF,并且包括许多简单、常用的运算符,例如许多相等运算符。查询规划器可以安全地允许在查询执行过程的任何点评估这些函数,因为在用户不可见的行上调用它们不会泄露有关不可见行的任何信息。此外,不带参数的函数或不从安全屏障视图接收任何参数的函数不需要标记为 LEAKPROOF 就可以被下推,因为它们永远不会从视图接收数据。相反,根据收到的参数值可能引发错误的函数(例如,在溢出或除以零的情况下引发错误的函数)不是 leakproof 的,并且在安全视图的行过滤器之前应用时,可能会泄露有关不可见行的重要信息。

例如,对于安全屏障视图(或具有行级安全策略的表)的查询,如果 WHERE 子句中使用的运算符与索引的运算符族相关联,但其底层函数未标记为 LEAKPROOF,则无法选择索引扫描。 psql 程序的 \dAo+ 元命令可用于列出运算符族并确定其中哪些运算符被标记为 leakproof。

重要的是要理解,即使是使用 security_barrier 选项创建的视图,其安全性也仅限于不可见元组的内容不会传递给可能不安全的函数。用户仍然可能有其他方法来推断不可见数据;例如,他们可以使用 EXPLAIN 查看查询计划,或测量针对视图的查询的运行时间。恶意攻击者可能能够推断出有关不可见数据量的某些信息,甚至可能获得有关数据分布或最常见值的一些信息(因为这些可能会影响计划的运行时间;甚至,因为它们也反映在优化器统计信息中,所以也会影响计划的选择)。如果担心这些类型的“隐蔽通道”攻击,最好根本不要授予对数据的任何访问权限。

提交更正

如果您在文档中看到任何不正确、与您在使用特定功能时的体验不符或需要进一步澄清的内容,请使用 此表格 报告文档问题。