支持的版本:当前 (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。规则系统会将 phone_numberSELECT 重写为 phone_dataSELECT。由于用户是 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_dataSELECT,并添加只有 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);

由于计划器会选择在更昂贵的 NOT LIKE 之前执行开销较小的 tricky 函数,因此 phone_data 表中的每个人和电话号码都将以 NOTICE 的形式打印出来。即使阻止用户定义新函数,也可以在类似的攻击中使用内置函数。(例如,大多数转换函数都会在其生成的错误消息中包含其输入值。)

类似的考虑也适用于更新规则。在上一节的示例中,示例数据库中的表的所有者可以授予其他人 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 即可向下推送。相反,一个可能根据接收到的参数值抛出错误的函数(例如,在发生溢出或除以零的情况下抛出错误的函数)不是防泄漏的,并且如果在安全视图的行过滤器之前应用,则可以提供有关未见行的重要信息。

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

提交更正

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