由于 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_number
的 SELECT
重写为来自 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
打印出来,因为规划器会选择在更昂贵的 NOT LIKE
之前执行廉价的 tricky
函数。 即使用户被禁止定义新函数,也可以在类似的攻击中使用内置函数。 (例如,大多数转换函数都会将它们的输入值包含在它们生成的错误消息中。)
类似的考虑适用于更新规则。 在上一节的示例中,示例数据库中表的拥有者可以授予其他人对 shoelace
视图的 SELECT
、INSERT
、UPDATE
和 DELETE
权限,但仅授予对 shoelace_log
的 SELECT
权限。 写入日志条目的规则操作仍将成功执行,并且该其他用户可以查看日志条目。 但他们不能创建假条目,也不能操纵或删除现有条目。 在这种情况下,不可能通过说服规划器更改操作顺序来破坏规则,因为唯一引用 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
查看查询计划,或测量针对视图的查询的运行时间。 恶意攻击者可能能够推断出关于不可见数据数量的信息,甚至获得有关数据分布或最常见值的一些信息(因为这些事情可能会影响计划的运行时间; 甚至,因为它们也反映在优化器统计信息中,计划的选择)。 如果这些类型的“秘密通道”攻击令人担忧,那么可能明智的做法是根本不授予任何对数据的访问权限。
如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符或需要进一步说明,请使用 此表格 报告文档问题。