PostgreSQL行级安全策略探究

前言

最近和朋友讨论oracle行级安全策略(VPD)时,查看了下官方文档,看起来VPD的原理是针对应用了Oracle行级安全策略的表、视图或同义词发出的 SQL 语句动态添加where子句。通俗理解就是将行级安全策略动态添加为where 条件。那么PG中的行级安全策略是怎么处理的呢?

行级安全简介

行级安全策略(Row Level Security)是更细粒度的数据安全控制策略。行级策略可以根据每个用户限制哪些行可以通过常规查询返回,哪些行可以通过数据修改命令插入、更新或删除。默认情况下,表没有任何行级安全策略,因此如果用户根据 SQL 权限系统具有表的访问权限,则其中的所有行都可以平等地用于查询或更新。

在PG中我们可以创建行级策略,在SQL执行时行级策略表达式将作为查询的一部分运行。
https://www.postgresql.org/docs/16/ddl-rowsecurity.html

行级安全演示

创建3个用户

postgres=# create user admin;
CREATE ROLE
postgres=# create user peter;
CREATE ROLE
postgres=# create user bob;
CREATE ROLE

创建一个rlsdb数据库

postgres=# create database rlsdb owner admin;
CREATE DATABASE

在rlsdb中使用admin用户创建表employee,并插入3个用户对应的数据

postgres=# \c rlsdb admin
You are now connected to database "rlsdb" as user "admin".
rlsdb=> create table employee ( empno int, ename text, address text, salary int, account_number text );
CREATE TABLE
rlsdb=> insert into employee values (1, 'admin', '2 down str',  80000, 'no0001' );
INSERT 0 1
rlsdb=> insert into employee values (2, 'peter', '132 south avn',  60000, 'no0002' );
INSERT 0 1
rlsdb=> insert into employee values (3, 'bob', 'Down st 17th',  60000, 'no0003' );
INSERT 0 1
rlsdb=> 

授权后,三个用户都能看到employee表的所有数据

rlsdb=> grant select on table employee to peter;
GRANT
rlsdb=> grant select on table employee to bob;
GRANT
rlsdb=> select * from employee;empno | ename |    address    | salary | account_number 
-------+-------+---------------+--------+----------------1 | admin | 2 down str    |  80000 | no00012 | peter | 132 south avn |  60000 | no00023 | bob   | Down st 17th  |  60000 | no0003
(3 rows)rlsdb=> 
rlsdb=> \c rlsdb peter
You are now connected to database "rlsdb" as user "peter".
rlsdb=> select * from employee;empno | ename |    address    | salary | account_number 
-------+-------+---------------+--------+----------------1 | admin | 2 down str    |  80000 | no00012 | peter | 132 south avn |  60000 | no00023 | bob   | Down st 17th  |  60000 | no0003
(3 rows)rlsdb=> \c rlsdb bob
You are now connected to database "rlsdb" as user "bob".
rlsdb=> select * from employee;empno | ename |    address    | salary | account_number 
-------+-------+---------------+--------+----------------1 | admin | 2 down str    |  80000 | no00012 | peter | 132 south avn |  60000 | no00023 | bob   | Down st 17th  |  60000 | no0003
(3 rows)

使用admin用户创建行级安全策略,对于peter和bob就只能看到自己的数据了。

rlsdb=> \c rlsdb admin
You are now connected to database "rlsdb" as user "admin".
rlsdb=> CREATE POLICY emp_rls_policy ON employee FOR ALL TO PUBLIC USING (ename=current_user);
CREATE POLICY
rlsdb=> ALTER TABLE employee ENABLE ROW LEVEL SECURITY;
ALTER TABLE
rlsdb=> \c rlsdb peter
You are now connected to database "rlsdb" as user "peter".
rlsdb=> select * from employee;empno | ename |    address    | salary | account_number 
-------+-------+---------------+--------+----------------2 | peter | 132 south avn |  60000 | no0002
(1 row)rlsdb=> \c rlsdb bob
You are now connected to database "rlsdb" as user "bob".
rlsdb=> select * from employee;empno | ename |   address    | salary | account_number 
-------+-------+--------------+--------+----------------3 | bob   | Down st 17th |  60000 | no0003
(1 row)rlsdb=>

行级安全原理

先看下行级安全策略在数据库中的呈现是什么样的。
查看pg_policy表,可以看到我们创建的emp_rls_policy这个策略,具体的策略polqual是一串字符,熟悉parsetree结构的朋友能关注到这是一个OPEXPR node。我们常见的where 条件也是类似的结构。

我们可以使用函数让polqual以更适合人阅读的方式来展示。

创建策略时,其实是将策略转换为where子句存到pg_policy表中。

ObjectAddress
CreatePolicy(CreatePolicyStmt *stmt)
{
/*省略部分代码行*/
/*将策略转化为where子句*/qual = transformWhereClause(qual_pstate,stmt->qual,EXPR_KIND_POLICY,"POLICY");with_check_qual = transformWhereClause(with_check_pstate,stmt->with_check,EXPR_KIND_POLICY,"POLICY");/* Fix up collation information */assign_expr_collations(qual_pstate, qual);assign_expr_collations(with_check_pstate, with_check_qual);
/* 将转换后的子句写入pg_policy*//* Open pg_policy catalog */pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);/* Set key - policy's relation id. */ScanKeyInit(&skey[0],Anum_pg_policy_polrelid,BTEqualStrategyNumber, F_OIDEQ,ObjectIdGetDatum(table_id));/* Set key - policy's name. */ScanKeyInit(&skey[1],Anum_pg_policy_polname,BTEqualStrategyNumber, F_NAMEEQ,CStringGetDatum(stmt->policy_name));sscan = systable_beginscan(pg_policy_rel,PolicyPolrelidPolnameIndexId, true, NULL, 2,skey);policy_tuple = systable_getnext(sscan);/* Complain if the policy name already exists for the table */if (HeapTupleIsValid(policy_tuple))ereport(ERROR,(errcode(ERRCODE_DUPLICATE_OBJECT),errmsg("policy \"%s\" for table \"%s\" already exists",stmt->policy_name, RelationGetRelationName(target_table))));policy_id = GetNewOidWithIndex(pg_policy_rel, PolicyOidIndexId,Anum_pg_policy_oid);values[Anum_pg_policy_oid - 1] = ObjectIdGetDatum(policy_id);values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(table_id);values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,CStringGetDatum(stmt->policy_name));values[Anum_pg_policy_polcmd - 1] = CharGetDatum(polcmd);values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(stmt->permissive);values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids);/* Add qual if present. */if (qual)values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual));elseisnull[Anum_pg_policy_polqual - 1] = true;/* Add WITH CHECK qual if present */if (with_check_qual)values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual));elseisnull[Anum_pg_policy_polwithcheck - 1] = true;policy_tuple = heap_form_tuple(RelationGetDescr(pg_policy_rel), values,isnull);CatalogTupleInsert(pg_policy_rel, policy_tuple);/* Record Dependencies */target.classId = RelationRelationId;target.objectId = table_id;target.objectSubId = 0;myself.classId = PolicyRelationId;myself.objectId = policy_id;myself.objectSubId = 0;recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,DEPENDENCY_NORMAL);recordDependencyOnExpr(&myself, with_check_qual,with_check_pstate->p_rtable, DEPENDENCY_NORMAL);/* Register role dependencies */target.classId = AuthIdRelationId;target.objectSubId = 0;for (i = 0; i < nitems; i++){target.objectId = DatumGetObjectId(role_oids[i]);/* no dependency if public */if (target.objectId != ACL_ID_PUBLIC)recordSharedDependencyOn(&myself, &target,SHARED_DEPENDENCY_POLICY);}InvokeObjectPostCreateHook(PolicyRelationId, policy_id, 0);/* Invalidate Relation Cache */CacheInvalidateRelcache(target_table);/* Clean up. */heap_freetuple(policy_tuple);free_parsestate(qual_pstate);free_parsestate(with_check_pstate);systable_endscan(sscan);relation_close(target_table, NoLock);table_close(pg_policy_rel, RowExclusiveLock);return myself;
}

在SQL执行时,查询重写阶段会将对应的安全策略拼接到parsetree里,最后生成执行计划去执行。
从执行计划来看SQL没有where条件,但是执行计划中存在 Filter: (ename = CURRENT_USER),证明了这个过程。

rlsdb=> explain analyze select * from employee ;QUERY PLAN                                              
-----------------------------------------------------------------------------------------------------Seq Scan on employee  (cost=0.00..19.15 rows=3 width=104) (actual time=0.010..0.012 rows=1 loops=1)Filter: (ename = CURRENT_USER)Rows Removed by Filter: 2Planning Time: 0.416 msExecution Time: 0.036 ms
(5 rows)rlsdb=>

再debug验证下这个过程。
给fireRIRrules函数设置断点,进入断点后从stack可以看到目前是在QueryRewrite阶段,结合一些规则进行查询重写。

观察这个时候的parsetree,可以看到还没有将安全策略对应的OPEXPR拼接进来。

等执行到get_row_security_policies函数已获取到表对应安全策略securityQuals。
打印securityQuals可以看到和我们查询pg_policy中的OPEXPR是一致的。

接着将securityQuals加入到rte的list中,这样我们再去打印parsetree就可以看到安全策略securityQuals对应的OPEXPR已经被拼接进来。

然后就是去生成执行计划并执行。

小结

PG的RLS也是将对应的策略动态转换为where子句,在查询重写阶段将安全策略拼接到parsetree,生成执行计划去执行。

行级安全策略,可以提供更精细粒度的表数据权限管理,在一定的场景下,比如只让用户看到自己对应的数据,能做到更安全的权限把控。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/45441.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用UDP通信接收与发送Mavlink2.0协议心跳包完整示例

1.克隆mavlink源码 https://github.com/mavlink/mavlink.git 2.进入mavlink目录,安装依赖 python3 -m pip install -r pymavlink/requirements.txt 3.生成Mavlink的C头文件 mavlink % python3 -m pymavlink.tools.mavgen --lang=C --wire-protocol=2.0 --output=generated…

1-5岁幼儿胼胝体的表面形态测量

摘要 胼胝体(CC)是大脑中的一个大型白质纤维束&#xff0c;它参与各种认知、感觉和运动过程。尽管CC与多种发育和精神疾病有关&#xff0c;但关于这一结构的正常发育(特别是在幼儿阶段)还有很多待解开的谜团。虽然早期文献中报道了性别二态性&#xff0c;但这些研究的观察结果…

【Linux网络】select{理解认识select/select与多线程多进程/认识select函数/使用select开发并发echo服务器}

文章目录 0.理解/认识回顾回调函数select/pollread与直接使用 read 的效率差异 1.认识selectselect/多线程&#xff08;Multi-threading&#xff09;/多进程&#xff08;Multi-processing&#xff09;select函数socket就绪条件select的特点总结 2.select下echo服务器封装套接字…

C++ 类和对象 赋值运算符重载

前言&#xff1a; 在上文我们知道数据类型分为自定义类型和内置类型&#xff0c;当我想用内置类型比较大小是非常容易的但是在C中成员变量都是在类(自定义类型)里面的&#xff0c;那我想给类比较大小那该怎么办呢&#xff1f;这时候运算符重载就出现了 一 运算符重载概念&…

安全防御:防火墙基本模块

目录 一、接口 1.1 物理接口 1.2 虚拟接口 二、区域 三、模式 3.1 路由模式 3.2 透明模式 3.3 旁路检测模式 3.4 混合模式 四、安全策略 五、防火墙的状态检测和会话表技术 一、接口 1.1 物理接口 三层口 --- 可以配置IP地址的接口 二层口&#xff1a; 普通二层…

车载终端_RTK定位|4路摄像头|驾驶辅助系统ADAS定制方案

现代车辆管理行业的发展趋势逐渐向智能化和高效化方向发展&#xff0c;车载终端成为关键的工具之一。在这个背景下&#xff0c;一款特别为车队管理行业设计的车载终端应运而生。该车载终端采用8寸多点触控电容屏&#xff0c;搭载联发科四核处理器&#xff0c;主频2.0GHz&#x…

如何安装node.js

Node.js Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境。 主要特点和优势&#xff1a; 非阻塞 I/O 和事件驱动&#xff1a;能够高效处理大量并发连接&#xff0c;非常适合构建高并发的网络应用&#xff0c;如 Web 服务器、实时聊天应用等。 例如&#xff0c;在…

网络安全——防御(防火墙)带宽以及双机热备实验

12&#xff0c;对现有网络进行改造升级&#xff0c;将当个防火墙组网改成双机热备的组网形式&#xff0c;做负载分担模式&#xff0c;游客区和DMZ区走FW3&#xff0c;生产区和办公区的流量走FW1 13&#xff0c;办公区上网用户限制流量不超过100M&#xff0c;其中销售部人员在其…

排序相关算法--3.选择排序

之前涉及的堆排序就是选择排序的一种&#xff0c;先进行选择。 基本选择排序&#xff1a; 最简单&#xff0c;也是最没用的排序算法&#xff0c;时间复杂度高并且还是不稳定的排序方法&#xff0c;项目中很少会用。 过程&#xff1a; 在一个长度为 N 的无序数组中&#xff0c;…

智慧公厕系统助力城市卫生管理

在当今快速发展的城市环境中&#xff0c;城市卫生管理面临着诸多挑战。其中&#xff0c;公共厕所的管理一直是一个重要但又常被忽视的环节。然而&#xff0c;随着科技的不断进步&#xff0c;智慧公厕系统的出现为城市卫生管理带来了全新的解决方案&#xff0c;成为提升城市品质…

OrangePi AIpro 浅上手

OrangePi AIpro 浅上手 OrangePi AIpro 介绍开发版介绍硬件规格顶层视图和底层视图接口详情图 玩转 OrangePi AIPro烧录镜像串口调试连接 WiFissh 连接配置下载源 使用感受优点&#xff1a;缺点或需注意的点&#xff1a; OrangePi AIpro 介绍 开发版介绍 OrangePi AIpro是香橙…

【大语言模型】私有化搭建-企业知识库-知识问答系统

下面是我关于大语言模型学习的一点记录 目录 人工智能学习路线 MaxKB 系统(基于大语言模型的知识问答系统) 部署开源大语言模型LLM 1.CPU模式(没有好的GPU&#xff0c;算力和效果较差) 2.GPU模式&#xff08;需要有NVIDIA显卡支持&#xff09; Ollama网络配置 Ollama前…

【问卷系统】TDucKX更新速览

TDuck是一款在线表单问卷收集工具&#xff0c;开源地址&#xff1a;https://gitee.com/TDuckApp一款免费的表单问卷系统&#xff1b;可快速创建问卷或业务表单&#xff0c;采用无代码理念支持开发自定义组件。采用SpringBootVueElementUI技术栈&#xff0c;功能强大界面清新&am…

Catena-x标准解读:CX-0007 Minimal Data Provider Service Offering v1.0.2 最小数据提供商服务产品

为了更好地理解&#xff0c;最小数据提供者服务也将被称为“上传工具”。 对于数据供应工具来说&#xff0c;数据主权的概念尤为重要。数据主权是Catena-X网络的核心价值观之一。每个参与者都应该尽可能多地控制自己的数据。这包括 他总是确切地知道他在与谁交换数据。参与者…

软件测试面试200问(全)

1、B/S架构和C/S架构区别 B/S 只需要有操作系统和浏览器就行&#xff0c;可以实现跨平台&#xff0c;客户端零维护&#xff0c;维护成本低&#xff0c;但是个性化能力低&#xff0c;响应速度较慢 C/S响应速度快&#xff0c;安全性强&#xff0c;一般应用于局域网中&#xff0…

【matlab】智能优化算法优化BP神经网络

目录 引言 一、BP神经网络简介 二、智能优化算法概述 三、智能优化算法优化BP神经网络的方法 四、蜣螂优化算法案例 1、算法来源 2、算法描述 3、算法性能 结果仿真 代码实现 引言 智能优化算法优化BP神经网络是一个重要的研究领域&#xff0c;旨在通过智能算法提高…

变量筛选—特征包含信息量

在变量筛选中,通过衡量特征所包含信息量大小,决定是否删除特征,常用的指标有单一值占比、缺失值占比和方差值大小。单一值或缺失值占比越高,表示特征包含信息量越少,不同公司设置不同阈值,一般单一值、缺失值占比高于95%,建议删除。方差值越小,代表特征包含信息量越小。…

入职前回顾一下git-01

git安装 Linux上安装git 在linux上建议用二进制的方式来安装git&#xff0c;可以使用发行版包含的基础软件包管理工具来安装。 红帽系 sudo yum install gitDebian系 sudo apt install gitWindows上安装git 去官网下载和操作系统位数相同的安装包.或者可以直接安装GitHub…

[图解]SysML和EA建模住宅安全系统-14-黑盒系统规约

1 00:00:02,320 --> 00:00:07,610 接下来&#xff0c;我们看下一步指定黑盒系统需求 2 00:00:08,790 --> 00:00:10,490 就是说&#xff0c;把这个系统 3 00:00:11,880 --> 00:00:15,810 我们的目标系统&#xff0c;ESS&#xff0c;看成黑盒 4 00:00:18,030 --> …