37.4. 规则和权限

由于PostgreSQL规则系统对查询的重写,非初始查询指定的其它表/视图被访问。 使用更新规则的时候,这可能包括对表的写权限。

重写规则并不拥有一个独立的所有者。 关系(表或视图)的所有者自动成为重写规则的缺省所有者。 PostgreSQLL 规则系统改变缺省的访问控制系统的特性。 因规则而使用的关系在(规则)重写时要对定义规则所有者进行权限检查, 而不是激活规则的用户这意味着一个用户只需要对他的查询里明确指定的表/视图拥有所需的权限就可进行操作。

例如:某用户有一个电话号码列表,其中一些是私人的,另外的一些是办公室秘书需要的。 他可以用下面方法构建查询:

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 secretary;

除了他以外(还有数据库超级用户)没有人可以访问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_data有相同字段的表并且每天拷贝一次数据进去。 那么这是他自己的数据因而可以赋予其它人访问的权力。 一个GRANT意味着"我信任你"。 如果某个你信任的人做了上面的事情,那你就该想想是否该REVOKE了。

需要注意的是,当视图可以用上面的技术来隐藏某些列的内容时,它们就不能可靠的在不可见的行上隐藏数据。 例如,下面的视图是不安全的:

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

这个视图看起来是安全的,因为规则系统会从phone_numberSELECT,然后重写入 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);

phone_data表中的每一个人和电话号码会被标记为NOTICE,因为规划器会选择在 执行NOT LIKE之前,先执行tricky,因为前者的开销大。 即使禁止用户自定义一个新函数,内置函数也可以用在类似的攻击中。例如,转换函数会包含在它们产生的错误信息中的输入。

这个机制同样可以用于更新规则。 在上一章的例子里,例子数据库里的表的所有者可以把shoelace视图的SELECTINSERTUPDATEDELETE权限赋予其他人。 但对shoelace_log只有SELECT权限。写日志记录的规则动作仍然可以成功的执行。 并且其它用户可以看到日志记录。但他不能创建伪记录,而且他也不能对现有记录进行修改或删除。 在这种情况下,不可能通过让规划器改变操作的顺序来更改规则,因为 只有指向shoelace_log的规则才是无限制的。