Postgresql 中文操作指南

41.5. Rules and Privileges #

由于 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 中的 SELECTphone_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 函数。即使阻止用户定义新函数,内置函数也可用于类似的攻击。(例如,大多数转换函数在其产生的错误消息中包含其输入值。)

类似的考虑适用于更新规则。在前一节的示例中,示例数据库中表的所有者可以将 SELECTINSERTUPDATEDELETE 权限授予 shoelace 视图上的其他人,但只能对 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 看到查询计划,或测量针对该视图的查询的运行时间。恶意攻击者可能会推断出一些关于不可见数据量的信息,甚至获得有关数据分布或最常见值的一些信息(因为这些事情可能会影响计划的运行时间;甚至因为它们也反映在优化程序统计信息中,所以计划的选择)。如果这些类型的“隐蔽信道”攻击令人担忧,那么最好不要授予对数据的任何访问权限。