PostfreSQL-行安全策略

除了通过 GRANT 提供的 SQL 标准权限系统之外,表还可以具有行安全策略,这些策略基于每个用户来限制正常查询可以返回哪些行,或者可以通过数据修改命令插入、更新或删除哪些行。此功能也称为行级安全性。默认情况下,表没有任何策略,因此如果用户根据 SQL 权限系统对表具有访问权限,则表中的所有行都可以同等地用于查询或更新。

当对表启用行安全性时(使用 ALTER TABLE ... ENABLE ROW LEVEL SECURITY),行安全策略必须允许对表的所有正常访问以选择行或修改行。(但是,表的所有者通常不受行安全策略的约束。)如果表不存在策略,则使用默认拒绝策略,这意味着没有行可见或可以修改。适用于整个表的操作,例如 TRUNCATE 和 REFERENCES,不受行安全性的约束。

行安全策略可以特定于命令、角色或两者。可以指定策略以应用于所有命令,或应用于 SELECT、INSERT、UPDATE 或 DELETE。可以将多个角色分配给给定策略,并且应用正常的角色成员资格和继承规则。

要根据策略指定哪些行可见或可修改,需要一个返回布尔结果的表达式。在来自用户查询的任何条件或函数之前,将为每一行评估此表达式。(此规则的唯一例外是leakproof函数,它们保证不会泄漏信息;优化器可以选择在行安全检查之前应用此类函数。)表达式不返回 true 的行将不被处理。可以指定单独的表达式以提供对可见行和允许修改的行的独立控制。策略表达式作为查询的一部分运行,并以运行查询的用户的权限运行,尽管安全定义函数可用于访问调用用户不可用的数据。

具有 BYPASSRLS 属性的超级用户和角色在访问表时总是绕过行安全系统。表所有者通常也会绕过行安全性,尽管表所有者可以选择使用 ALTER TABLE ... FORCE ROW LEVEL SECURITY 来接受行安全性。

启用和禁用行安全性以及向表添加策略始终仅是表所有者的权限。

使用 CREATE POLICY 命令创建策略,使用 ALTER POLICY 命令更改策略,使用 DROP POLICY 命令删除策略。要启用和禁用给定表的行安全性,请使用 ALTER TABLE 命令。

每个策略都有一个名称,并且可以为一个表定义多个策略。由于策略是特定于表的,因此表的每个策略都必须具有唯一的名称。不同的表可能有同名的策略。

当多个策略应用于给定查询时,它们使用 OR(对于默认的许可策略)或 AND(对于限制性策略)进行组合。这类似于给定角色拥有其所属的所有角色的权限的规则。

举个简单的例子,这里是如何在账户关系上创建一个策略,只允许managers角色的成员访问行,并且只允许account表账户的行:

CREATE TABLE accounts (manager text, company text, contact_email text);
ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;
CREATE POLICY account_managers ON accounts TO managers
USING (manager = current_user);

如果未指定角色,或者使用了特殊用户名 PUBLIC,则该策略适用于系统上的所有用户。要允许所有用户仅访问users表中他们自己的行,可以使用一个简单的策略:

CREATE POLICY user_policy ON users
USING (user_name = current_user);

要对添加到表中的行与那些可见的行使用不同的策略,可以组合多个策略。这对策略将允许所有用户查看 users 表中的所有行,但只能修改自己的:

CREATE POLICY user_sel_policy ON users
FOR SELECT
USING (true);

CREATE POLICY user_mod_policy ON users
USING (user_name = current_user);

在 SELECT 命令中,这两个策略使用 OR 组合在一起,最终结果是可以选择所有行。在其他命令类型中,仅适用第二条策略,因此效果与之前相同。

也可以使用 ALTER TABLE 命令禁用行安全性。禁用行安全不会删除表上定义的任何策略;他们只是被忽略了。然后表中的所有行都是可见和可修改的,受标准 SQL 权限系统的约束。

下面是一个更大的示例,说明如何在生产环境中使用此功能。表 passwd 模拟 Unix 密码文件:

CREATE TABLE passwd (
  user_name             text UNIQUE NOT NULL,
  pwhash                text,
  uid                   int  PRIMARY KEY,
  gid                   int  NOT NULL,
  real_name             text NOT NULL,
  home_phone            text,
  extra_info            text,
  home_dir              text NOT NULL,
  shell                 text NOT NULL
);

CREATE ROLE admin; 
CREATE ROLE bob;
CREATE ROLE alice;

INSERT INTO passwd VALUES
  ('admin','xxx',0,0,'Admin','111-222-3333',null,'/root','/bin/dash');
INSERT INTO passwd VALUES
  ('bob','xxx',1,1,'Bob','123-456-7890',null,'/home/bob','/bin/zsh');
INSERT INTO passwd VALUES
  ('alice','xxx',2,1,'Alice','098-765-4321',null,'/home/alice','/bin/zsh');

ALTER TABLE passwd ENABLE ROW LEVEL SECURITY;

CREATE POLICY admin_all ON passwd TO admin USING (true) WITH CHECK (true);

CREATE POLICY all_view ON passwd FOR SELECT USING (true);

CREATE POLICY user_mod ON passwd FOR UPDATE
USING (current_user = user_name)
WITH CHECK (
  current_user = user_name AND
  shell IN ('/bin/bash','/bin/sh','/bin/dash','/bin/zsh','/bin/tcsh')
);

GRANT SELECT, INSERT, UPDATE, DELETE ON passwd TO admin;

GRANT SELECT
(user_name, uid, gid, real_name, home_phone, extra_info, home_dir, shell)
ON passwd TO public;

GRANT UPDATE
(pwhash, real_name, home_phone, extra_info, shell)
ON passwd TO public;

测试:

set role admin;

table passwd;

set role alice;

table passwd;

select user_name,real_name,home_phone,extra_info,home_dir,shell from passwd;

update passwd set user_name = 'joe';

update passwd set real_name = 'Alice Doe';

update passwd set real_name = 'John Doe' where user_name = 'admin';

update passwd set shell = '/bin/xx';

delete from passwd;

insert into passwd (user_name) values ('xxx');

update passwd set pwhash = 'abc';

迄今为止构建的所有策略都是许可策略,这意味着当应用多个策略时,它们会使用“OR”布尔运算符进行组合。虽然可以将许可策略构建为仅允许在预期情况下访问行,但将许可策略与限制策略(记录必须通过并使用“AND”布尔运算符组合)组合起来会更简单。在上面的示例的基础上,我们添加了一个限制性策略,要求管理员通过本地 Unix 套接字连接以访问 passwd 表的记录:

CREATE POLICY admin_local_only ON passwd AS RESTRICTIVE TO admin
USING (pg_catalog.inet_client_addr() IS NULL);

然后我们可以看到,由于限制性策略,通过网络连接的管理员将看不到任何记录:(连接时带上 -h 127.0.0.1)

set role admin;

SELECT current_user;

select inet_client_addr();

TABLE passwd;

UPDATE passwd set pwhash = NULL;

引用完整性检查(例如唯一或主键约束和外键引用)始终绕过行安全性以确保维护数据完整性。在开发模式和行级策略时必须小心,以避免通过此类参照完整性检查“隐蔽通道”信息泄漏。

在某些情况下,确保未应用行安全性很重要。例如,在进行备份时,如果行安全性默默地导致某些行从备份中被忽略,这可能是灾难性的。在这种情况下,您可以将 row_security 配置参数设置为 off。这本身并没有绕过行安全性;如果任何查询的结果被策略过滤,它的作用是抛出错误。然后可以调查并修复错误的原因。

posted @ 2022-09-03 14:04  shigp1  阅读(66)  评论(0编辑  收藏  举报