學習如何在PostgreSQL中管理安全性

學習如何在PostgreSQL中管理安全性


  • 來源 | 願碼(ChainDesk.CN)內容編輯
  • 願碼Slogan | 鏈接每一個程序員的故事
  • 網站 | http://chaindesk.cn
  • 願碼願景 | 打造全學科IT系統免費課程,助力小白用戶、初級工程師0成本免費系統學習、低成本進階,幫助BAT一線資深工程師成長並利用自身優點創造睡後收入。
  • 官方公衆號 | 願碼 | 願碼服務號 | 區塊鏈部落
  • 免費加入願碼全思惟工程師社羣 | 任一公衆號回覆「願碼」兩個字獲取入羣二維碼

本文閱讀時長:17min程序員

在處理安全性時,記住這些級別以便以有組織的方式處理與安全相關的問題。sql

· 綁定地址:listen_addresses在文件中 postgresql.conf數據庫

· 基於主機的訪問控制:pg_hba.conf文件安全

· 實例級權限:用戶,角色,數據庫建立,登陸和複製服務器

· 數據庫級權限:鏈接,建立模式等網絡

· 架構級權限:使用架構並在架構內建立對象架構

· 表級權限:選擇,插入,更新等socket

· 列級權限:容許或限制對列的訪問post

· 行級安全性:限制對行的訪問學習

爲了讀取值, PostgreSQL必須確保咱們在每一個級別都有足夠的權限。整個權限鏈必須是正確的。

在本文中,您將學習處理SSL,列級安全性和配置默認權限等的過程。

瞭解綁定地址和鏈接


配置PostgreSQL服務器時,須要作的第一件事就是定義遠程訪問。默認狀況下,PostgreSQL不接受遠程鏈接。重要的是因爲PostgreSQL不能監聽端口,因此致使它接收不到鏈接。若是咱們嘗試鏈接,錯誤消息實際上未來自操做系統。

假設有一個使用默認配置的數據庫服務器192.168.0.123,將發生如下狀況:

iMac:~ hs$ telnet 192.168.0.123 5432 
Trying 192.168.0.123... 
telnet: connect to address 192.168.0.123: Connection refused 
telnet: Unable to connect to remote host

Telnet嘗試在端口上建立鏈接,5432並當即被遠程框拒絕。從外面看,看起來PostgreSQL根本就沒有運行。

成功鏈接的關鍵能夠在postgresql.conf文件中找到:

# - Connection Settings -

# shop_addresses ='localhost'
# 要監聽的IP地址;
# 逗號分隔的地址列表;
# defaults爲'localhost'; 使用'*'表示全部
# (更改須要重啓)

該listen_addresses設置將告訴PostgreSQL要監聽的地址。從技術上講,這些地址是綁定地址。這究竟意味着什麼?假設咱們的機器中有四個IP地址。咱們這些IP地址只監聽其中的三個。PostgreSQL只考慮這三個IP的請求,而不考慮第四個。

若是咱們放入 *,PostgreSQL會監聽分配給您機器的每一個IP。

可是,有更多與鏈接管理相關的設置對於理解很是重要。它們以下:

#port = 5432 
              # (change requires restart) 
max_connections = 100 
              # (change requires restart) 
# Note: Increasing max_connections costs ~400 bytes of 
# shared memory per 
# connection slot, plus lock space 
# (see max_locks_per_transaction). 
#superuser_reserved_connections = 3 
              # (change requires restart) 
#unix_socket_directories = '/tmp' 
              # comma-separated list of directories 
              # (change requires restart) 
#unix_socket_group = '' 
              # (change requires restart) 
#unix_socket_permissions = 0777 
              # begin with 0 to use octal notation 
              # (change requires restart)

首先,PostgreSQL偵聽單個TCP端口,默認值爲5432。請記住,PostgreSQL只會偵聽單個端口。每當有請求進入時,postmaster將建立一個新進程來處理鏈接。默認狀況下,最多容許100個普通鏈接。最重要的是,爲超級用戶保留了三個額外的鏈接。這意味着咱們能夠擁有97個鏈接加上三個超級用戶或100個超級用戶鏈接。

處理SSL


PostgreSQL容許咱們加密服務器和客戶端之間的傳輸。加密很是有用,特別是若是咱們進行遠距離通訊。SSL提供了一種簡單而安全的方式來確保沒有人可以收聽您的通訊。

在本節中,咱們將學習如何設置SSL。

首先要作的是在服務器啓動時將ssl參數設置on爲postgresql.conf文件。在下一步中,咱們能夠將SSL證書放入$PGDATA目錄中。若是咱們不但願證書位於其餘目錄中,請更改如下參數:

#ssl_cert_file = 'server.crt'    # (change requires restart) 
#ssl_key_file = 'server.key'     # (change requires restart) 
#ssl_ca_file = ''                # (change requires restart) 
#ssl_crl_file = ''               # (change requires restart)

若是咱們要使用自簽名證書,請執行如下步驟:

openssl req -new -text -out server.req

回答OpenSSL提出的問題。確保咱們輸入本地主機名做爲通用名稱。咱們能夠將密碼留空。此調用將生成一個受密碼保護的密鑰; 它不會接受長度少於四個字符的密碼短語。

要刪除密碼(若是要自動啓動服務器,則必須如此),請運行如下命令:

openssl rsa -in privkey.pem -out server.key
rm privkey.pem

輸入舊密碼以解鎖現有密鑰。如今,執行此操做將證書轉換爲自簽名證書,並將密鑰和證書複製到服務器將查找的位置:

openssl req -x509 -in server.req -text
  -key server.key -out server.crt

執行此操做後,請確保文件具備正確的權限集:

chmod og-rwx server.key

一旦將適當的規則放入pg_hba.conf文件中,咱們就可使用SSL鏈接到您的服務器。要驗證咱們確實使用SSL,請考慮簽出該pg_stat_ssl功能。它將告訴咱們每一個鏈接以及它是否使用SSL,它將提供有關加密的一些重要信息:

test=# \d pg_stat_sslView  "pg_catalog.pg_stat_ssl"
Column | Type | Modifiers -------------+----------+----------- pid | integer | ssl | boolean | version | text | cipher | text | bits | integer | compression | boolean |

clientdn     | text     |

若是ssl進程的字段包含true; PostgreSQL作了咱們指望它作的事情:

postgres=# select * from pg_stat_ssl; 
-[ RECORD 1 ] 
---------------------------- 
pid         | 20075
ssl         | t 
version     | TLSv1.2
cipher      | ECDHE-RSA-AES256-GCM-SHA384 
bits        | 256
compression | f 
clientdn    |

處理實例級安全性

到目前爲止,咱們已經配置了綁定地址,咱們告訴PostgreSQL使用哪一種IP範圍進行身份驗證。到目前爲止,配置純粹與網絡相關。

在下一步中,咱們能夠將注意力轉移到實例級別的權限。最重要的是要知道PostgreSQL中的用戶存在於實例級別。若是咱們建立一個用戶,它不只在一個數據庫中可見; 它能夠被全部數據庫看到。用戶可能只具備訪問單個數據庫的權限,但基本上用戶是在實例級別建立的。

對於那些剛接觸PostgreSQL的人來講,還有一件事要記住:用戶和角色是一回事。CREATE ROLE和CREATE USER子句有不一樣的默認值(字面上,惟一的區別是LOGIN默認狀況下角色沒有獲得屬性),但在一天結束時,用戶和角色是相同的。所以,CREATE ROLE和CREATE USER子句支持徹底相同的語法:

test=# \h CREATE USER
Command: CREATE USER
Description: define a new database role
Syntax:
CREATE USER name [ [ WITH ] option [ ... ] ]
where option can be:

SUPERUSER | NOSUPERUSER
| CREATEDB | NOCREATEDB
| CREATEROLE | NOCREATEROLE
| INHERIT | NOINHERIT
| LOGIN | NOLOGIN
| REPLICATION | NOREPLICATION
| BYPASSRLS | NOBYPASSRLS
| CONNECTION LIMIT connlimit
| [ ENCRYPTED ] PASSWORD 'password'
| VALID UNTIL 'timestamp'
| IN ROLE role_name [, ...]
| IN GROUP role_name [, ...]
| ROLE role_name [, ...]
| ADMIN role_name [, ...]
| USER role_name [, ...]
| SYSID uid

讓咱們逐個討論這些語法元素。咱們首先看到的是用戶能夠是超級用戶或普通用戶。若是某人被標記爲a SUPERUSER ,則再也不有普通用戶必須面對的任何限制。A SUPERUSER 能夠根據須要刪除對象(數據庫等)。

下一個重要的事情是它在實例級別上獲取建立新數據庫的權限。

規則是這樣的:建立者老是自動成爲對象的全部者(除非另有說明,不然可使用該CREATE DATABASE子句)。美麗的是,對象全部者也能夠再次丟棄一個對象。

下一個重要的是INHERIT/ NOINHERITclause。若是INHERIT設置了子句(這是默認值),則用戶能夠繼承其餘用戶的權限。使用繼承權限容許咱們使用角色,這是抽象權限的好方法。例如,咱們能夠建立角色bookkeeper並使許多其餘角色繼承bookkeeper。咱們的想法是bookkeeper,即便咱們有不少人從事會計工做,咱們也只須要告訴PostgreSQL一次容許作什麼。

該LOGIN/ NOLOGIN子句定義一個角色是否能夠登陸到該實例。

在理論介紹以後,是時候實際建立用戶並看看在實際示例中如何使用事物:

test=# CREATE ROLE  bookkeeper NOLOGIN;  
CREATE ROLE 
test=# CREATE ROLE  joe LOGIN;  
CREATE ROLE 
test=# GRANT  bookkeeper TO joe;  
GRANT ROLE

這裏作的第一件事bookkeeper是建立一個名爲的角色。

請注意,咱們不但願人們以身份登陸bookkeeper,所以角色標記爲NOLOGIN。

另請注意,NOLOGIN若是使用該CREATE ROLE子句,則爲默認值。若是您更喜歡該CREATE USER子句,則默認設置爲LOGIN。

而後,joe建立角色並標記爲LOGIN。最後,將bookkeeper角色分配給joe角色,以便他能夠執行bookkeeper實際容許的全部操做。

一旦用戶到位,咱們能夠測試到目前爲止咱們擁有的內容:

[hs@zenbook ~]$ psql test -U bookkeeper 
psql: FATAL:  role "bookkeeper" is not permitted to log in

正如所料,該bookkeeper角色不容許登陸系統。若是joe角色嘗試登陸會發生什麼?

[hs@zenbook ~]$ psql test -U joe
    ... 
    test=>

這實際上將按預期工做。但請注意,命令提示符已更改。這只是PostgreSQL向您顯示您未以超級用戶身份登陸的一種方式。

建立用戶後,可能須要對其進行修改。咱們可能想要改變的一件事是密碼。在PostgreSQL中,容許用戶更改本身的密碼。下面是它的工做原理:

test=> ALTER  ROLE  joe PASSWORD 'abc';  
ALTER  ROLE 
test=> SELECT current_user; 
 current_user 
--------------  
 joe 
(1 row)

該ALTER ROLE條款(或ALTER USER)將容許咱們改變它能夠建立用戶時設置大多數設置。可是,管理用戶還有不少。在許多狀況下,咱們但願爲用戶分配特殊參數。該ALTER USER條款爲咱們提供了這樣作的方法:

ALTER ROLE { role_specification | ALL } 
     [ IN DATABASE database_name ] 
              SET configuration_parameter { TO | = } { value | DEFAULT } 
ALTER ROLE { role_specification | ALL } 
     [ IN DATABASE database_name ] 
              SET configuration_parameter FROM CURRENT 
ALTER ROLE { role_specification | ALL } 
     [ IN DATABASE database_name ] RESET configuration_parameter 
ALTER ROLE { role_specification | ALL } 
     [ IN DATABASE database_name ] RESET ALL

語法很是簡單,很是簡單。爲了描述爲何這很是有用,我添加了一個真實的例子。讓咱們假設Joe碰巧住在毛里求斯島上。當他登陸時,即便他的數據庫服務器位於歐洲,他也但願本身在他本身的時區:

test=> ALTER  ROLE  joe SET TimeZone = 'UTC-4';  
ALTER  ROLE 
test=> SELECT now(); 
             now 
------------------------------- 
2017-01-09 20:36:48.571584+01 
(1 row) 
 
test=> q 
[hs@zenbook ~]$ psql  test  -U joe 
... 
test=> SELECT now(); 
           now 
------------------------------- 
2017-01-09 23:36:53.357845+04 
(1 row)

該ALTER ROLE子句將修改用戶。一旦joe從新鏈接,就會爲他設置時區。

時區不會當即更改。您應該從新鏈接或使用SET ... TO DEFAULT子句。

這裏重要的是這對於某些內存參數也是可能的,例如work_mem等等。

數據庫級別的安全性


在實例級別配置用戶以後,能夠深刻挖掘並查看在數據庫級別能夠執行的操做。出現的第一個主要問題是:咱們明確容許Joe登陸數據庫實例,可是誰或什麼容許Joe實際鏈接到其中一個數據庫?也許咱們不但願Joe訪問系統中的全部數據庫。限制對某些數據庫的訪問正是咱們在此級別上能夠實現的目標。

對於數據庫,可使用GRANT子句設置如下權限:

GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] 
     | ALL [ PRIVILEGES ] } 
    ON DATABASE database_name [, ...] 
    TO role_specification [, ...] [ WITH GRANT OPTION ]

數據庫級別有兩個主要權限值得密切關注:

· CREATE:這容許某人在數據庫中建立模式。請注意,CREATE子句不容許建立表; 它是關於模式的。在PostgreSQL中,表位於模式中,所以您必須首先進入模式級別才能建立表。

· CONNECT:這容許有人鏈接到數據庫。

如今的問題是:沒有人明確CONNECT爲joe角色分配權限,那麼這些權限實際上來自何處?答案是:有一個叫作的東西public,相似於Unix世界。若是世界被容許作某事,那麼joe,誰是通常公衆的一部分。

最重要的是,public它不是一個角色,它能夠被刪除和重命名。咱們能夠簡單地將其視爲系統中每一個人的等價物。

所以,爲了確保不是每一個人均可以隨時鏈接到任何數據庫,CONNECT可能必須從公衆中撤銷。爲此,咱們能夠以超級用戶身份進行鏈接並解決問題:

[hs@zenbook ~]$ psql  test  -U postgres 
... 
test=# REVOKE ALL ON DATABASE test FROM public;  
REVOKE 
test=# \q 
[hs@zenbook ~]$ psql test -U joe 
psql:  FATAL:  permission denied for database "test"  
DETAIL:  User does not have CONNECT privilege.

咱們能夠看到,該joe角色再也不容許鏈接。此時,只有超級用戶才能進行測試。

一般,postgres 即便在建立其餘數據庫以前,最好仍是從數據庫中撤消權限。這個概念背後的想法是,這些權限將再也不存在於全部新建立的數據庫中。若是某人須要訪問某個數據庫,則必須明確授予權限。權利再也不自動存在。

若是咱們想容許joe角色鏈接到測試數據庫,請以超級用戶身份嘗試如下行:

[hs@zenbook ~]$ psql test -U postgres 
... 
test=# GRANT CONNECT ON DATABASE test TO bookkeeper;  
GRANT 
test=# \q 
[hs@zenbook ~]$ psql test -U joe 
...  
test=>

基本上,這裏有兩種選擇:

· 咱們能夠joe直接容許角色,以便只有joe角色才能鏈接。

· 或者,咱們能夠爲該bookkeeper角色授予權限。請記住,joe角色將繼承角色的全部權限bookkeeper,所以,若是咱們但願全部會計師都可以鏈接到數據庫,則爲角色分配權限bookkeeper彷佛是一個有吸引力的想法。

若是咱們爲該bookkeeper角色授予權限,則它沒有風險,由於該角色不容許首先登陸到該實例,所以它純粹做爲權限來源。

處理列級安全性


在某些狀況下,並非每一個人均可以看到全部數據。想象一下銀行,有些人可能會看到有關銀行賬戶的所有信息,而其餘人可能僅限於數據的一部分。在現實世界的狀況下,可能不容許某人閱讀餘額欄,或者有人可能看不到人民貸款的利率。

另外一個例子是,人們能夠看到人們的我的資料,但不能看到他們的照片或其餘私人信息。如今的問題是:如何使用列級安全性?

爲了證實這一點,咱們將向屬於該joe角色的現有表添加一列:

test=> ALTER TABLE t_useful ADD COLUMN name text;  
ALTER TABLE

該表如今由兩列組成。該示例的目標是確保用戶只能看到其中一列:

test=> \d t_useful 
    Table  "public.t_useful"  
 Column |   Type   | Modifiers 
--------+---------+-----------  
 id     | integer  | 
 name   | text     |

做爲超級用戶,讓咱們建立一個用戶並讓它訪問包含咱們表的模式:

test=# CREATE ROLE paul LOGIN;  
CREATE ROLE 
test=# GRANT CONNECT ON DATABASE test TO paul;  
GRANT 
test=# GRANT USAGE ON SCHEMA public TO paul;  
GRANT

CONNECT被撤銷了public。所以,明確授予絕對是必要的,以確保咱們甚至能夠到達桌面。

該SELECT權限能夠賦予paul角色:

test=# GRANT  SELECT (id)  ON t_useful TO paul;  
GRANT

基本上,這已經足夠了。已經能夠以用戶身份鏈接到數據庫paul並閱讀該列:

[hs@zenbook ~]$ psql test -U paul 
... 
test=> SELECT id FROM t_useful; 
id 
---- 
(0 rows)

若是咱們使用列級權限,請記住一件重要的事情,咱們應該中止使用SELECT *,由於它再也不起做用了:

test=> SELECT * FROM t_useful; 
ERROR:  permission denied for relation t_useful
  • 仍然意味着全部列,但因爲沒法訪問全部列,事情將當即出錯。

配置默認權限


到目前爲止,已經配置了不少東西。若是將新表添加到系統會發生什麼?逐個處理這些表並設置適當的權限可能會很是痛苦和風險。若是這些事情會自動發生,那不是很好嗎?這正是該ALTER DEFAULT PRIVILEGES條款的做用。這個想法是爲用戶提供一個選項,讓PostgreSQL在對象出現後當即自動設置所需的權限。有人不能再忘記設置這些權利了。

如下清單顯示了語法規範的第一部分:

postgres=# \h ALTER DEFAULT PRIVILEGES
Command: ALTER DEFAULT PRIVILEGES
Description: define default access privileges
Syntax:
ALTER DEFAULT PRIVILEGES
    [ FOR { ROLE | USER } target_role [, ...] ]
    [ IN SCHEMA schema_name [, ...] ]
    abbreviated_grant_or_revoke
where abbreviated_grant_or_revoke is one of:

GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] }
ON TABLES
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
...

基本上,語法與GRANT子句相似,所以使用起來簡單直觀。爲了向咱們展現它的工做原理,我編寫了一個簡單的例子。咱們的想法是,若是joe角色建立了一個表,該paul角色將自動使用它:

test=# ALTER DEFAULT PRIVILEGES FOR ROLE joe 
      IN SCHEMA public GRANT ALL ON TABLES TO paul;  
ALTER DEFAULT PRIVILEGES

讓咱們joe如今做爲角色鏈接並建立一個表:

[hs@zenbook ~]$ psql  test  -U joe 
... 
test=> CREATE TABLE  t_user (id serial, name  text,  passwd text);  
CREATE TABLE

做爲paul角色鏈接將證實該表已分配給適當的權限集:

[hs@zenbook ~]$ psql test -U paul 
... 
test=> SELECT * FROM  t_user; 
 id | name | passwd 
----+------+--------  
(0 row
相關文章
相關標籤/搜索