40.5. 规则和特权

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

重写规则没有单独的所有者。关系(表或视图)的所有者自动是为其定义的重写规则的所有者。 PostgreSQL 规则系统更改默认访问控制系统的行为。将根据规则所有者(而不是调用规则的用户)的特权来检查由于规则而使用的关系。这意味着用户只需要其查询中显式命名的表/视图所需的特权。

例如:用户有一个电话 Numbers 列表,其中一些是私人电话 Numbers,其他电话 Numbers 是办公室助理感兴趣的。用户可以构造以下内容:

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进行了检查,但这是针对调用用户的检查,因此,除了用户和助手之外,没有人可以使用它。

将按规则检查特权。因此,助手是目前唯一可以看到公用电话 Numbers 的人。但是助手可以设置另一个视图,并向公众授予对该视图的访问权限。然后,任何人都可以通过助手的视图查看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表中的每个人和电话 Numbers 都将打印为NOTICE,因为计划人员将选择在价格较贵的NOT LIKE之前执行价格便宜的tricky功能。即使阻止用户定义新功能,也可以在类似的攻击中使用内置功能。 (例如,大多数转换函数将其 Importing 值包含在它们产生的错误消息中.)

类似的注意事项适用于更新规则。在上一节的示例中,示例数据库中表的所有者可以向其他人授予shoelace视图上的SELECTINSERTUPDATEDELETE特权,但仅向shoelace_log授予SELECT特权。写入日志条目的规则操作仍将成功执行,并且其他用户可以看到日志条目。但是他们不能创建假条目,也不能操纵或删除现有条目。在这种情况下,不可能通过说服计划者更改操作 Sequences 来颠覆规则,因为唯一引用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创建的视图可能比不使用此选项创建的视图效果更差。通常,没有办法避免这种情况:如果可能影响安全性的最快计划必须予以拒绝。因此,默认情况下不启用此选项。

查询计划器在处理没有副作用的函数时具有更大的灵 Active。此类函数称为LEAKPROOF,并且包括许多简单的常用运算符,例如许多相等运算符。查询计划者可以安全地允许在查询执行过程中的任何时候对这些函数进行评估,因为在用户不可见的行上调用它们不会泄漏任何与看不见的行有关的信息。此外,不接受自变量或未从安全屏障视图传递任何自变量的函数不必标记为LEAKPROOF即可下推,因为它们从不从该视图接收数据。相反,一个函数可能会根据作为参数接收的值而引发错误(例如,在溢出或被零除时会引发错误的函数)不是防泄漏的,并且可以提供有关看不见的行的重要信息如果在安全视图的行过滤器之前应用。

重要的是要理解,即使使用security_barrier选项创建的视图也仅在不可见的 Tuples 的内容不会传递给可能不安全的函数的有限意义上是安全的。用户很可能还有其他方式可以推断未见数据。例如,他们可以使用EXPLAIN查看查询计划,或者根据视图衡量查询的运行时间。恶意攻击者可能能够推断出一些看不见的数据量,甚至可以获得一些有关数据分布或最常用值的信息(因为这些情况可能会影响计划的运行时间;甚至,因为它们也会被反映出来)在优化程序统计信息中,选择计划)。如果需要考虑这些类型的“隐蔽通道”攻击,则完全不授予任何对数据的访问权限可能是不明智的。