PostgreSQL 9.4 patch : Row-Level Security

前段时间写过一篇关于使用视图来提供行级别的数据保护, 当创建视图时如果未使用security_barriers, 那么这个视图是不安全的, 攻击者可以利用低成本函数打印出隐藏的基表数据. 使用security_barriers可以规避这个问题, 但是牺牲了SQL优化器的作用, 查询将会变成seq scan, 全表扫描.

感兴趣的朋友可以参见如下BLOG : 

http://blog.163.com/digoal@126/blog/static/163877040201361031431669/

本文讲述的是将要在9.4发布的行级别安全补丁RLS. 在数据保护方面和视图效果一样, 同时不会有security_barriers带来的弊端.

这个补丁尚未提交, 所以安装时需要注意.

首先下载一个PostgreSQL devel版本. 补丁在处理nodeFuncs.c时目前有点小问题, 使用以下snapshot可以正常打补丁.

http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=10a509d82956dee14eb2011bd266cd3c728ae188

下载补丁文件 :

wget http://www.postgresql.org/message-id/attachment/29700/pgsql-v9.4-row-level-security.v3b.patch

打补丁

tar -zxvf postgresql-10a509d.tar.gz
cd postgresql-10a509d
patch -p1 < ./pgsql-v9.4-row-level-security.v3b.patch

安装

./configure --prefix=/home/pg94/pgsql9.4devel --with-pgport=1921 --with-perl --with-tcl --with-python --with-openssl --with-pam --without-ldap --with-libxml --with-libxslt --enable-thread-safety --with-wal-blocksize=16 && gmake && gmake install

初始化数据库

initdb -E UTF8 -D $PGDATA --locale=C -W -U postgres
pg_ctl start

语法

ALTER TABLE
    SET ROW SECURITY FOR rowsec_command TO (condition)
    RESET ROW SECURITY FOR rowsec_command
and rowsec_command is:
    { ALL | SELECT | INSERT | UPDATE | DELETE }

测试 : 

创建测试表

digoal=# create table test (id int, info text);
CREATE TABLE
digoal=# insert into test select generate_series(1,1000), md5(random()::text);
INSERT 0 1000

设置行安全策略

digoal=# alter table test SET ROW SECURITY FOR select TO (id<=999);
ERROR:  0A000: Row-security for "select" is not implemented yet
LOCATION:  ATExecSetRowSecurity, pg_rowsecurity.c:305
digoal=# alter table test SET ROW SECURITY FOR insert TO (id<=999);
ERROR:  0A000: Row-security for "insert" is not implemented yet
LOCATION:  ATExecSetRowSecurity, pg_rowsecurity.c:305
digoal=# alter table test SET ROW SECURITY FOR update TO (id<=999);
ERROR:  0A000: Row-security for "update" is not implemented yet
LOCATION:  ATExecSetRowSecurity, pg_rowsecurity.c:305
digoal=# alter table test SET ROW SECURITY FOR delete TO (id<=999);
ERROR:  0A000: Row-security for "delete" is not implemented yet
LOCATION:  ATExecSetRowSecurity, pg_rowsecurity.c:305

目前只支持all commands.

digoal=# alter table test SET ROW SECURITY FOR all TO (id<=999);
ALTER TABLE

超级用户不受限制.

digoal=# select * from test  where id>=998;
  id  |               info
------+----------------------------------
  998 | 7177340c488270f432b1476d001f3b9d
  999 | a609aef006b1147dad10f3e43993dfea
 1000 | c7fa1acdd43d442be5a940c9f7091abc
(3 rows)
digoal=# create role digoal nosuperuser nocreatedb login encrypted password 'digoal';
CREATE ROLE
digoal=# grant select on test to digoal;
GRANT

普通用户受到安全限制. id=1000的不会查出来.

digoal=# \c digoal digoal
You are now connected to database "digoal" as user "digoal".
digoal=> select * from test  where id>=998;
 id  |               info
-----+----------------------------------
 998 | 7177340c488270f432b1476d001f3b9d
 999 | a609aef006b1147dad10f3e43993dfea
(2 rows)

从执行计划可以看到已经自动增加了安全限制条件id<=999

digoal=> explain analyze select * from test  where id>=998;
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Seq Scan on test  (cost=0.00..24.00 rows=1 width=37) (actual time=0.271..0.271 rows=2 loops=1)
   Filter: ((id <= 999) AND (id >= 998))
   Rows Removed by Filter: 998
 Total runtime: 0.308 ms
(4 rows)

使用RLS不会像视图的security_barriers那样无法使用优化器. 所以索引是有效的.

digoal=> \c digoal postgres
You are now connected to database "digoal" as user "postgres".
digoal=# create index idx_test_1 on test(id);
CREATE INDEX
digoal=# \c digoal digoal
You are now connected to database "digoal" as user "digoal".
digoal=> explain analyze select * from test  where id>=998;
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Index Scan using idx_test_1 on test  (cost=0.28..2.29 rows=1 width=37) (actual time=0.007..0.008 rows=2 loops=1)
   Index Cond: ((id <= 999) AND (id >= 998))
 Total runtime: 0.065 ms
(3 rows)

attack测试 : 

digoal=# \c digoal digoal
You are now connected to database "digoal" as user "digoal".
digoal=> create or replace function attack(test) returns boolean as $$
digoal$> declare
digoal$> begin
digoal$>   raise notice '%', $1;
digoal$>   return true;
digoal$>
digoal$> end;
digoal$> $$ language plpgsql strict cost 0.000000000000001;
CREATE FUNCTION
digoal=> select * from test where id>997 and attack(test);
NOTICE:  (998,7177340c488270f432b1476d001f3b9d)
NOTICE:  (999,a609aef006b1147dad10f3e43993dfea)
 id  |               info
-----+----------------------------------
 998 | 7177340c488270f432b1476d001f3b9d
 999 | a609aef006b1147dad10f3e43993dfea
(2 rows)
digoal=> explain analyze verbose select * from test where id>997 and attack(test);
NOTICE:  (998,7177340c488270f432b1476d001f3b9d)
NOTICE:  (999,a609aef006b1147dad10f3e43993dfea)
                                                              QUERY PLAN                                                            

------------------------------------------------------------------------------------------------------------------------------------
--
 Subquery Scan on test  (cost=0.28..2.33 rows=1 width=37) (actual time=0.113..0.138 rows=2 loops=1)
   Output: test.id, test.info
   Filter: attack(test.test)
   ->  Index Scan using idx_test_1 on public.test test_1  (cost=0.28..2.31 rows=2 width=98) (actual time=0.014..0.018 rows=2 loops=1
)
         Output: test_1.id, test_1.info, test_1.*
         Index Cond: ((test_1.id <= 999) AND (test_1.id > 997))
 Total runtime: 0.343 ms
(7 rows)
digoal=> \c digoal postgres
You are now connected to database "digoal" as user "postgres".
digoal=# drop index idx_test_1;
DROP INDEX
digoal=# \c digoal digoal
You are now connected to database "digoal" as user "digoal".
digoal=> select * from test where id>997 and attack(test);
NOTICE:  (998,7177340c488270f432b1476d001f3b9d)
NOTICE:  (999,a609aef006b1147dad10f3e43993dfea)
 id  |               info
-----+----------------------------------
 998 | 7177340c488270f432b1476d001f3b9d
 999 | a609aef006b1147dad10f3e43993dfea
(2 rows)
digoal=> explain analyze verbose select * from test where id>997 and attack(test);
NOTICE:  (998,7177340c488270f432b1476d001f3b9d)
NOTICE:  (999,a609aef006b1147dad10f3e43993dfea)
                                                     QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Subquery Scan on test  (cost=0.00..24.02 rows=1 width=37) (actual time=0.381..0.403 rows=2 loops=1)
   Output: test.id, test.info
   Filter: attack(test.test)
   ->  Seq Scan on public.test test_1  (cost=0.00..24.00 rows=2 width=98) (actual time=0.289..0.292 rows=2 loops=1)
         Output: test_1.id, test_1.info, test_1.*
         Filter: ((test_1.id <= 999) AND (test_1.id > 997))
         Rows Removed by Filter: 998
 Total runtime: 0.439 ms
(8 rows)

从执行计划可以看出设置RLS后, RLS的条件作为子查询, attack(test)在子查询外面. 所以不可能从attack中窥探子查询外的数据, 因此id=1000的数据在这里是看不到的.

[参考]
1. http://www.postgresql.org/message-id/flat/CADyhKSWGtZqpsXtF7_q2FvKRvX6RqW+xv7VmxNmj4gubSBoo-g@mail.gmail.com

2. http://www.pgcon.org/2013/schedule/attachments/273_PGcon2013-kaigai-row-level-security.pdf

3. https://github.com/kaigai/sepgsql/tree/rowsec

4. http://wiki.postgresql.org/wiki/RLS

5. http://blog.163.com/digoal@126/blog/static/163877040201361031431669/

时间: 2024-10-29 20:51:45

PostgreSQL 9.4 patch : Row-Level Security的相关文章

RLS(Row Level Security) 在 PostgreSQL 9.5 中的使用

PostgreSQL 9.5 更新增加了许多新的功能,比如增加新的JSONB函数,新的GROUPING函数等.RLS(Row Level Security)功能在此次更新中也得到相应的提升.RLS是一种表格行安全机制,利用为每一个表加上你需要使用数据库角色作为一个主要的安全机制. 以下是一个RLS示例: 创建表.修改表.创建策略:使用RLS CREATE TABLE ratings2 ( user_role_name NAME, rating_type_name TEXT, artist_nam

PostgreSQL Fine-Grained Table,Column,Row Level Audit

通过配置用户级或数据库级的参数可以实现用户以及数据库级别的审计, 但是这样的粒度可能还是太粗糙了. 如果需要更细致的审计, 例如针对某些表的操作审计, 某些用户对某些表的审计, 或者仅仅当某个列的值发生变化时才被审计(记录到LOG或表里面, 本文的例子是将审计信息输出到LOG, 使用raise). 这样的需求可以通过触发器来实现. 接下来以PostgreSQL 9.2为例进行讲解. # 基础的参数配置 log_destination = 'csvlog' logging_collector =

PostgreSQL 9.4 patch, Obtaining the call stack context information in plpgsql

PostgreSQL 9.4 新增plpgsql补丁, 该补丁增加了调用堆信息的输出. 可以用于plpgsql debug等. 测试 : pg94@db-192-168-100-216-> psql psql (9.4devel) Type "help" for help. digoal=# -- access to call stack digoal=# create or replace function inner_func(int) digoal-# returns in

PostgreSQL 9.5 新特性汇总 [转]

PostgreSQL 9.5快发布了,很多给力的功能. 原文 https://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.5 (This page is currently under development ahead of the release of PostgreSQL 9.5) This page contains an overview of PostgreSQL Version 9.5's features, incl

PostgreSQL SQL 语言:数据定义

本文档为PostgreSQL 9.6.0文档,本转载已得到原译者彭煜玮授权. 1.表基础 关系型数据库中的一个表非常像纸上的一张表:它由行和列组成.列的数量和顺序是固定的,并且每一列拥有一个名字.行的数目是变化的,它反映了在一个给定时刻表中存储的数据量.SQL并不保证表中行的顺序.当一个表被读取时,表中的行将以非特定顺序出现,除非明确地指定需要排序.这些将在Chapter 7介绍.此外,SQL不会为行分配唯一的标识符,因此在一个表中可能会存在一些完全相同的行.这是SQL之下的数学模型导致的结果,

一天学会PostgreSQL应用开发与管理 - 5 数据定义

背景 本章大纲 1. 数据类型 2. 数据操作 3. 表管理 4. 视图 5. 约束 6. RLS(行安全策略) 第三章:数据定义 1. 数据类型 https://www.postgresql.org/docs/9.6/static/datatype.html 1.数值 Name Storage Size Description Range smallint 2 bytes small-range integer -32768 to +32767 integer 4 bytes typical

PostgreSQL on ECS SLA 流复制备库+秒级快照+PITR+自动清理

标签 PostgreSQL , ECS , 阿里云 , 部署 , 物理镜像 , 流复制 , 快照备份 , 备份验证 , 自动清理 背景 介绍在阿里云ECS环境中,实现一个非常简单,但是可用性和可靠性满足一般企业要求的PostgreSQL环境. 包括: 1.自动启动数据库 2.包括一个物理流复制备库 3.包括自动的秒级快照备份 4.包括自动备份集有效性验证 5.包括自动清理N天以前的备份集.归档文件 6.监控请自建 部署环境介绍 1.ECS 111.111.111.199 (主) 111.111.

Sql Server 2016新功能之Row-Level Security(值得关注)_MsSql

Sql Server 2016 有一个新功能叫 Row-Level Security ,大概意思是行版本的安全策略(原来我是个英语渣_(:з」∠)_) 直接上例子.这个功能相当通过对表添加一个函数作为过滤规则,使得拥有不同条件的用户(或者登录名) 之类的,只能获取到符合条件的数据.相对来说是提供了那么一点的便捷性,当然也增加了数据的安全性,相当于每个用户连接进来只能看到 符合规则的数据(当然,这里的用户只是一个举例.其实是可以通过编写过滤函数来实现的) 举个例子 有三个用户 Sales1 ,Sa

Sql Server 2016新功能之Row-Level Security(值得关注)

Sql Server 2016 有一个新功能叫 Row-Level Security ,大概意思是行版本的安全策略(原来我是个英语渣_(:з」∠)_) 直接上例子.这个功能相当通过对表添加一个函数作为过滤规则,使得拥有不同条件的用户(或者登录名) 之类的,只能获取到符合条件的数据.相对来说是提供了那么一点的便捷性,当然也增加了数据的安全性,相当于每个用户连接进来只能看到 符合规则的数据(当然,这里的用户只是一个举例.其实是可以通过编写过滤函数来实现的) 举个例子 有三个用户 Sales1 ,Sa