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.pswp.cn/web/45441.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45441.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45441.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

搭建基于 ChatGPT 的問答系統

搭建基于 ChatGPT 的問答系統 &#x1f4e3;1.簡介&#x1f4e3;2.語言模型&#xff0c;提問范式和 token?2.1語言模型?2.2Tokens?2.3Helper function輔助函數&#xff08;提問范式&#xff09; &#x1f4e3;3.評估輸入-分類&#x1f4e3;4.檢查輸入-審核?4.1審核4.1.1 我…

使用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; 普通二層…

Java面試題:分庫分表

分庫分表 當數據量非常大時,就需要通過分庫分表的方式進行壓力分攤,避免數據庫訪問壓力過大 分庫分表的前提: 業務數據達到一定量級:單表數據量達到1000w或20g 優化解決不了性能問題 分庫分表策略 垂直拆分 垂直分庫 以表為依據,根據業務將不同表拆分到不同庫中 eg:根…

車載終端_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;在…

FeignClient詳解

FeignClient 是 Spring Cloud Open Feign 中的一個注解&#xff0c;它用于定義一個 Feign 客戶端&#xff0c;Feign 是一個聲明式的 Web 服務客戶端&#xff0c;使得編寫 Web 服務客戶端變得更加簡單。以下是 FeignClient 注解的詳細說明&#xff1a; 定義 Feign 客戶端&#x…

網絡安全——防御(防火墻)帶寬以及雙機熱備實驗

12&#xff0c;對現有網絡進行改造升級&#xff0c;將當個防火墻組網改成雙機熱備的組網形式&#xff0c;做負載分擔模式&#xff0c;游客區和DMZ區走FW3&#xff0c;生產區和辦公區的流量走FW1 13&#xff0c;辦公區上網用戶限制流量不超過100M&#xff0c;其中銷售部人員在其…

Swift入門筆記

Swift入門筆記 簡單值控制流函數和閉包對象和類枚舉和結構體并發協議和擴展錯誤處理泛型 簡單值 // 聲明變量 var myVariable 42 myVariable 50// 聲明常量 let myConstant 42// 聲明類型 let implicitInteger 70 let implicitDouble 70.0 let explicitDouble: Double 7…

排序相關算法--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網絡的核心價值觀之一。每個參與者都應該盡可能多地控制自己的數據。這包括 他總是確切地知道他在與誰交換數據。參與者…

【GameFramework擴展應用】6-4、GameFramework框架增加AB包加解密功能

推薦閱讀 CSDN主頁GitHub開源地址Unity3D插件分享簡書地址QQ群:398291828大家好,我是佛系工程師☆恬靜的小魔龍☆,不定時更新Unity開發技巧,覺得有用記得一鍵三連哦。 一、前言 【GameFramework框架】系列教程目錄: https://blog.csdn.net/q764424567/article/details/1…