云貝教育 |【技術(shù)文章】PostgreSQL 基礎(chǔ)知識(shí):對(duì)象所有權(quán)和默認(rèn)權(quán)限
在上一篇 PostgreSQL 基礎(chǔ)知識(shí):角色和特權(quán)文章中,討論了如何根據(jù)需要在 PostgreSQL 中創(chuàng)建和管理角色(用戶和組)。根據(jù)您的權(quán)限背景,特別是在其他數(shù)據(jù)庫(kù)產(chǎn)品中,權(quán)限工作方式中的一些細(xì)微差別可能令人驚訝。
理解角色和特權(quán)在 Postgres 中如何工作,是理解下一個(gè)更令人困惑的安全部分:對(duì)象所有權(quán)的關(guān)鍵。盡管在 Postgres 中可以分配許多特權(quán),但對(duì)象所有權(quán)具有特定級(jí)別的特權(quán),不能轉(zhuǎn)移給其他角色。隨著時(shí)間的推移,了解其含義對(duì)于管理數(shù)據(jù)庫(kù)架構(gòu)以及訪問(wèn)其中包含的對(duì)象至關(guān)重要。
一、誰(shuí)擁有數(shù)據(jù)庫(kù)對(duì)象?
在 PostgreSQL 中,創(chuàng)建對(duì)象(表、視圖、函數(shù)等)的角色成為所有者。它可以事后更改,但最初,創(chuàng)建者是所有者。我們可以使用交互式psql終端或查詢pg_catalog對(duì)象類型對(duì)應(yīng)的表來(lái)查看數(shù)據(jù)庫(kù)中對(duì)象的所有者。

正如我們所見(jiàn),表的所有者被設(shè)置為user1,因?yàn)樵摻巧珓?chuàng)建了它。
\d中的元命令是在幕后psql 執(zhí)行以下查詢,向我們展示關(guān)系列表(包括我們創(chuàng)建的表)以及誰(shuí)擁有每個(gè)關(guān)系。
提示:如您所見(jiàn),PostgreSQL 中的目錄包含很多有用的信息。然而,學(xué)習(xí)如何有效地查詢它們需要時(shí)間和經(jīng)驗(yàn)。這就是psql元命令特別有用并且在 PostgreSQL 開(kāi)發(fā)人員中非常流行的原因。
我已經(jīng)能聽(tīng)到你在想什么了。
“你一直說(shuō)對(duì)象所有者很重要,但你沒(méi)有說(shuō)為什么這很重要!當(dāng)然,對(duì)同一模式具有適當(dāng)權(quán)限的任何其他角色都可以使用該表。對(duì)嗎?”
關(guān)于對(duì)象所有權(quán),您需要了解三個(gè)要點(diǎn):
1、只有超級(jí)用戶(superuser)或?qū)ο?表、函數(shù)、過(guò)程、序列等)的所有者才能ALTER/DROP對(duì)象。
2、只有超級(jí)用戶(superuser)或?qū)ο蟮乃姓卟拍芨脑搶?duì)象的所有權(quán)。
3、只有對(duì)象的所有者可以為他們創(chuàng)建的對(duì)象定義默認(rèn)權(quán)限。
讓我們看看詳情,以更好地描述所有權(quán)和特權(quán)如何一起發(fā)揮作用,以及您可以做些什么來(lái)在 PostgreSQL 中主動(dòng)管理它。
二、設(shè)置用于演示的用戶和組
對(duì)于以下示例,我們將假設(shè)您的 Postgres 實(shí)例具有名稱為postgres的標(biāo)準(zhǔn)超級(jí)用戶主體。然后,我們將從上一篇文章中得到啟發(fā),設(shè)置兩個(gè)開(kāi)發(fā)用戶和一個(gè)開(kāi)發(fā)組,以便更輕松地管理權(quán)限。
這將返回:

運(yùn)行這條SQL后,數(shù)據(jù)庫(kù)有兩個(gè)可以登錄數(shù)據(jù)庫(kù)的開(kāi)發(fā)人員,每個(gè)都是devgrp組角色的成員。我們已授予允許成員在公共模式中創(chuàng)建對(duì)象的組權(quán)限,并且他們擁有所有表的所有基本 DML 權(quán)限。
現(xiàn)在讓我們通過(guò)觀察這個(gè)開(kāi)發(fā)團(tuán)隊(duì)開(kāi)始實(shí)施新功能來(lái)探索如何克服 PostgreSQL 中的幾個(gè)常見(jiàn)安全問(wèn)題。
1. 問(wèn)題 #1:改變對(duì)象
第一位開(kāi)發(fā)人員已準(zhǔn)備好深入研究新項(xiàng)目,跟蹤各種網(wǎng)絡(luò)的社交媒體用戶句柄。首先,他們創(chuàng)建了一個(gè)表來(lái)存儲(chǔ) Twitter 和 Facebook 句柄。
在 psql 中,使用“describe”元命令:
這將返回:

正如預(yù)期的那樣,該表已創(chuàng)建并歸 dev1 所有。隨著開(kāi)發(fā)人員著手開(kāi)發(fā)該功能,他們很快意識(shí)到一個(gè)新的社交網(wǎng)絡(luò)越來(lái)越受歡迎,他們需要為此跟蹤用戶句柄。dev2 提議添加新列以保持進(jìn)展。
希望這第一個(gè)簡(jiǎn)單的示例有助于闡明為什么對(duì)象所有權(quán)在您的 PostgreSQL 模式中如此重要。沒(méi)有可以授予第二個(gè)開(kāi)發(fā)人員允許他們對(duì)表進(jìn)行修改的特權(quán)。更改對(duì)象是為所有者(或超級(jí)用戶)保留的固有特權(quán)。
最常見(jiàn)的解決方案是:將所有對(duì)象的所有權(quán)設(shè)置為一致的角色,然后將該角色的成員資格授予需要修改對(duì)象的用戶。在我們的示例設(shè)置中,合理的選擇是角色devgrp,因?yàn)樗虚_(kāi)發(fā)人員都是該角色的成員。在更復(fù)雜的環(huán)境和開(kāi)發(fā)團(tuán)隊(duì)結(jié)構(gòu)中,您可能需要?jiǎng)?chuàng)建更多的組來(lái)適當(dāng)?shù)毓芾硭袡?quán)和訪問(wèn)權(quán)。
為了提供一個(gè)使用我們小型開(kāi)發(fā)團(tuán)隊(duì)的示例,我們可以將此表的所有者更改為所有開(kāi)發(fā)人員都是其成員的組,在我們的例子中是角色devgrp。更改所有者后,dev2應(yīng)該能夠更改它,因?yàn)樗麄兪窃摻M的成員。
此查詢的輸出是:

或者,您可以在創(chuàng)建對(duì)象之前臨時(shí)將會(huì)話的角色設(shè)置為公共所有者角色(假設(shè)您是該角色的成員)。創(chuàng)建的任何對(duì)象都將由創(chuàng)建時(shí)有效的角色擁有。為了演示,我將刪除表并再次嘗試相同的過(guò)程,但這次在創(chuàng)建表之前設(shè)置角色。
在 psql 中,使用“describe”元命令:
現(xiàn)在你會(huì)看到:

現(xiàn)在,作為第二個(gè)開(kāi)發(fā)用戶
這將返回:

這里的關(guān)鍵是所有權(quán)是維護(hù)和更改 PostgreSQL 數(shù)據(jù)庫(kù)中對(duì)象的重要組成部分。無(wú)論您是在創(chuàng)建對(duì)象之前設(shè)置角色還是在事后更改所有權(quán),只有擁有對(duì)象的角色成員才能更改對(duì)象。
2. 問(wèn)題 #2:默認(rèn)對(duì)象權(quán)限
我們通過(guò)將表所有者設(shè)置為所有開(kāi)發(fā)人員都是其成員的角色來(lái)解決第一個(gè)問(wèn)題。本質(zhì)上,對(duì)象的所有者類似于該對(duì)象的超級(jí)用戶。
但是,當(dāng)我們向數(shù)據(jù)庫(kù)添加一個(gè)將用于報(bào)告或只讀目的的新角色時(shí)會(huì)發(fā)生什么?
開(kāi)發(fā)團(tuán)隊(duì)已決定需要一個(gè)新角色來(lái)促進(jìn)此新功能(將生成的某些數(shù)據(jù)的報(bào)告功能)。
這會(huì)導(dǎo)致錯(cuò)誤:
鑒于我們目前所了解的情況,這應(yīng)該不足為奇。新rptusr角色是在表存在之后創(chuàng)建的,并且沒(méi)有被授予對(duì)該表的任何權(quán)限。對(duì)象的超級(jí)用戶或所有者必須明確授予必要的權(quán)限。
這將返回:

在本系列的第一篇文章中,我們將確保用戶僅擁有最低限度必要權(quán)限的過(guò)程稱為最小權(quán)限原則。一個(gè)對(duì)象一個(gè)對(duì)象地設(shè)置權(quán)限很快就會(huì)成為一項(xiàng)令人厭煩的任務(wù)。
添加組角色也沒(méi)有幫助,因?yàn)橥瑯拥膯?wèn)題仍然存在。特權(quán)只授予GRANT時(shí)存在的對(duì)象。換句話說(shuō),GRANT不是前瞻性的行動(dòng)。相反,我們需要一種方法讓PostgreSQL在每次創(chuàng)建對(duì)象時(shí)都應(yīng)用特權(quán)。
輸入默認(rèn)權(quán)限。
每個(gè)角色都可以創(chuàng)建一組默認(rèn)訪問(wèn)權(quán)限,只要他們?cè)谔囟〝?shù)據(jù)庫(kù)中創(chuàng)建對(duì)象,就會(huì)應(yīng)用這些權(quán)限。這為每個(gè)角色提供了完全控制,確保每次都使用正確的權(quán)限創(chuàng)建對(duì)象。
為了說(shuō)明這一點(diǎn),讓我們先創(chuàng)建新的默認(rèn)訪問(wèn)權(quán)限,然后再創(chuàng)建rptuser應(yīng)該能夠查詢的另一個(gè)新表。
首先,使用 psql 檢查是否沒(méi)有默認(rèn)訪問(wèn)權(quán)限:
在我的演示服務(wù)器上,這顯示沒(méi)有默認(rèn)訪問(wèn)權(quán)限。
接下來(lái),我們將安全上下文設(shè)置為您要為其設(shè)置默認(rèn)權(quán)限的組,當(dāng)他們?cè)谀承┠J街袆?chuàng)建新對(duì)象時(shí)將應(yīng)用這些權(quán)限。
再次檢查是否創(chuàng)建了默認(rèn)權(quán)限:
這將返回:

我們可以看到創(chuàng)建了默認(rèn)訪問(wèn)權(quán)限,該權(quán)限將為在模式中創(chuàng)建的任何表授予SELECT(讀?。┙巧珯?quán)限。為了驗(yàn)證它是否有效,我們現(xiàn)在可以創(chuàng)建一個(gè)新表并嘗試從中查詢,因?yàn)闊o(wú)需使用grant語(yǔ)句進(jìn)行額外干預(yù)。
這將返回:

成功!devgrp能夠創(chuàng)建表,新的rptusr能夠從表中進(jìn)行選擇,而不會(huì)出現(xiàn)錯(cuò)誤。接下來(lái),只要devgrp創(chuàng)建并擁有表(我們的示例對(duì)象),rptusr就能夠從中進(jìn)行選擇。
不幸的是,我們只解決了這個(gè)名為 rptusr 的“只讀”角色的問(wèn)題。一旦另一個(gè)只讀用戶需要訪問(wèn)數(shù)據(jù)庫(kù)對(duì)象,我們就必須授予現(xiàn)有表的權(quán)限,然后為將來(lái)的操作創(chuàng)建另一個(gè)默認(rèn)訪問(wèn)權(quán)限。這不是很可持續(xù),只是強(qiáng)調(diào)了我們?cè)谏弦黄恼轮杏懻摰膬?nèi)容。
一種常見(jiàn)的處理方法是:創(chuàng)建只讀組角色并為其設(shè)置默認(rèn)訪問(wèn)權(quán)限。然后,當(dāng)在數(shù)據(jù)庫(kù)中創(chuàng)建新的只讀用戶時(shí),他們可以被授予只讀組角色的成員資格,從而繼承相同的權(quán)限。
首先,使用以下psql命令檢查當(dāng)前的默認(rèn)訪問(wèn)權(quán)限:
這將返回:

現(xiàn)在創(chuàng)建新組和安全設(shè)置:
再次檢查是否創(chuàng)建了默認(rèn)權(quán)限:
這將返回以下內(nèi)容:

此時(shí),devgrp用戶創(chuàng)建的任何表都將應(yīng)用此默認(rèn)訪問(wèn)權(quán)限,并且該read_only角色的所有成員都將能夠查詢數(shù)據(jù)。
對(duì)于只讀用戶,PostgreSQL 14+ 確實(shí)提供了新的默認(rèn)角色以更輕松地管理對(duì)表數(shù)據(jù)的訪問(wèn)。在多租戶環(huán)境中正確設(shè)置它有足夠的細(xì)微差別,可以在將來(lái)單獨(dú)寫(xiě)一篇文章。
三、大規(guī)模管理所有權(quán)和特權(quán)
在前兩篇文章中,我們已經(jīng)介紹了很多關(guān)于 PostgreSQL 角色和安全性的基礎(chǔ)知識(shí)。
1、PostgreSQL 有角色。用戶和組是同義詞。
2、按照慣例,用戶角色可以登錄,而組角色不能。
3、超級(jí)用戶繞過(guò)所有權(quán)限檢查,可以訪問(wèn)或修改任何對(duì)象和集群設(shè)置。
4、除了對(duì)象所有者或超級(jí)用戶之外,每個(gè)角色都必須專門授予(或撤銷)對(duì)數(shù)據(jù)和對(duì)象的訪問(wèn)權(quán)限。
5、角色的權(quán)限可以通過(guò)繼承的方式被授予其他角色。
6、所有角色都具有自動(dòng)的、不可撤銷的PUBLIC角色成員資格
7、對(duì)象的所有者(或超級(jí)用戶)是唯一可以訪問(wèn)ALTER該DROP對(duì)象的角色。
8、每個(gè)角色都可以為他們創(chuàng)建的任何對(duì)象(如表)創(chuàng)建默認(rèn)訪問(wèn)權(quán)限,這樣其他角色就可以自動(dòng)訪問(wèn),而不是在每次創(chuàng)建對(duì)象時(shí)都應(yīng)用權(quán)限。
如果對(duì)PostgreSQL安全性來(lái)說(shuō)您是新手,那么需要考慮和管理的信息很多,這使得在更大的團(tuán)隊(duì)中管理 PostgreSQL 權(quán)限變得具有挑戰(zhàn)性。盡早制定計(jì)劃或啟動(dòng)模板來(lái)管理 PostgreSQL 權(quán)限和對(duì)象所有權(quán)是很有幫助的第一步。很多次,我看到團(tuán)隊(duì)在不了解和沒(méi)有安全計(jì)劃的情況下開(kāi)始一個(gè)項(xiàng)目,通常是因?yàn)樗麄儧](méi)有完全理解我們一直在討論的事情。
也就是說(shuō),這是您開(kāi)始時(shí)就要考慮的示例角色和安全設(shè)置。使用您對(duì)我們目前討論的內(nèi)容(以及官方文檔)的了解,結(jié)合您組織的要求,修改和制定適合的方案。
在下一篇文章中,我們將了解如何在每次創(chuàng)建新數(shù)據(jù)庫(kù)時(shí)將所有這些放在一起以創(chuàng)建一組權(quán)限模板。這將確保您擁有跨數(shù)據(jù)庫(kù)和角色的一致、可重現(xiàn)的訪問(wèn)權(quán)限。
四、結(jié)論
PostgreSQL 的安全和所有權(quán)模型可能會(huì)讓新用戶感到困惑。了解角色的創(chuàng)建方式、權(quán)限的分配方式以及隨著時(shí)間的推移如何為一致的訪問(wèn)準(zhǔn)備數(shù)據(jù)庫(kù)將幫助您有效地使用 PostgreSQL 并讓您的團(tuán)隊(duì)順暢地工作。