Oracle最佳替代者PostgreSQL數據庫的整體安全性
數據庫安全性是如今基于 Web 的應用程序面對的最大挑戰。如果不加以控制,您將面臨公司敏感信息被曝光的風險,更糟糕的是,珍貴的客戶信息也將面臨被泄露的風險。在本文中,了解可以用來保護您的 PostgreSQL 數據庫的安全措施。
簡介
報紙上經常出現黑客襲擊企業數據庫的報道。大多數攻擊由叛逆的未成年人發起的日子已經一去不復返。如今,數據收集成為了一項重要的事業,并且由在企業基礎設施中工作的專家專門負責。問題已經不再是您如何 阻止未授權的訪問企圖 — 您無法阻止 — 而在于您在發生這種情況時如何降低 影響。
本文討論了在保護您的 PostgreSQL(也稱為 Postgres)數據庫服務器時遇到的挑戰。PostgreSQL 是一款強大的開源對象-關系數據庫系統。它擁有一個可靠的架構并以可靠性、數據完整性和準確性著稱。它運行在所有主流操作系統之上,包括 Linux®、UNIX® 和 Windows®。它與 ACID 完全兼容,并且充分支持外鍵、連接、視圖、觸發器和存儲過程(支持多種語言)。
理想的管理員
在傳統的 UNIX 中,PostgreSQL 被進行了重新設計來補充它所依附的操作系統。要最大限度地發掘 PostgreSQL 的價值,所需要的知識超過了一般數據庫管理員(DBA)所要求具備的技能。
簡單來說,一名合格的 PostgreSQL DBA 需要具備以下背景:
•了解關系理論并熟悉 SQL'92, '99, 和 2003。
•知道如何閱讀源代碼,特別是 C 代碼,并且能夠在 Linux 上編譯源代碼。
•能夠管理系統并熟悉 system-V UNIX 或 Linux。
•能夠維護(如果需要的話)IT 組織中出現的各種典型硬件項目。理解 TCP OS 層,能夠將網絡分為子網,調優防火墻,等等。
許 多 DBA 只具備管理、監控和調優數據庫本身的技能。然而,PostgreSQL 在構建時也考慮了 OS 工具。當然,很少有 DBA 精通所有學科的知識,但是擁有這些知識使 PostgreSQL DBA 能夠用更少的時間完成更多的工作,而通過其他方式是無法辦到的。
訪問權限回顧
如果您要了解可能的攻擊媒介(attack vector),那么了解數據庫角色的作用是非常重要的。首先,您需要通過授予和撤銷權限來控制對數據的訪問。
角色、授予權限和特權
一個具有默認權限和特權的普通角色的安全性究竟如何?用戶帳戶可以通過以下命令的其中之一創建:
•SQL 語句 CREATE USER
•SQL 語句 CREATE ROLE
•Postgres 命令行實用程序 createuser
這三種創建用戶帳戶的方法表現出不同的行為,并導致產生截然不同的默認權限和特權。
對于一個普通角色,典型的用戶可以執行下面的操作:
•如果數據集群使用如 pg_hba.conf 中描述的默認身份驗證策略,那么用戶可以訪問任何數據庫。
•在用戶可以訪問的任何數據庫的 PUBLIC 模式中創建對象。
•在臨時會話中創建會話(臨時)對象,比如模式 pg_temp_?
•修改運行時參數。
•創建用戶定義函數
•執行在 PUBLIC 模式中由其他用戶創建的用戶定義函數(只要處理的是用戶有權訪問的對象 。
一定要清楚用戶所具有的權限,但是,了解普通用戶在默認情況下不可以做什么也同樣重要。普通用戶無權執行以下操作:
•創建數據庫或模式。
•創建其他用戶。
•訪問由其他用戶創建的對象。
•登錄(只適合語句 CREATE ROLE)。
超級用戶權限和特權
盡管普通用戶無權執行被定義為超級用戶功能的權限和特權,但是普通用戶仍然會引起一些與默認權限和特權有關的問題。
本文將討論一些可由普通用戶操作的攻擊媒介。
訪問對象
一項極其常見且不太安全的實踐發生在將 PostgreSQL 用作 Web 服務器的后端時。開發人員創建普通用戶的目的只是讓其使用 INSERT、UPDATE 和 DELETE 命令來執行數據操作命令。然而,未經授權的操作也可能會被執行,因為 PUBLIC 模式是公開給所有人的。例如,用戶可以對這些表進行數據挖掘。甚至還可以對表進行修改:添加規則和觸發器、將數據保存到 PUBLIC 模式中的表,隨后這些數據就會被收集。
記住,一個被盜用的用戶帳戶可以對它所擁有的對象做任何事情。
#p#
對這類威脅的反擊很簡單:不要讓普通用戶訪問或創建任何內容。清單 1 展示了如何對一個表提供保護。
清單 1. 對表提供保護
postgres=# SET SESSION AUTHORIZATION postgres;SETpostgres=# CREATE ROLE user1 WITH LOGIN UNENCRYPTED PASSWORD '123';CREATE ROLEpostgres=# CREATE SCHEMA user1 CREATE TABLE t1(i int);CREATE SCHEMApostgres=# INSERT INTO user1.t1 VALUES(1);INSERT 0 1postgres=# GRANT USAGE ON SCHEMA user1 TO user1;GRANTpostgres=# SELECT I FROM user1.t1; i--- 2(1 row)postgres=# SET SESSION AUTHORIZATION user1;SETpostgres=> SELECT I FROM user1.t1;ERROR: permission denied for relation t1postgres=> SET SESSION AUTHORIZATION postgres;SETpostgres=# GRANT SELECT ON user1.t1 TO user1;GRANTpostgres=# SET SESSION AUTHORIZATION user1;SETpostgres=> SELECT I FROM user1.t1; i--- 2(1 row)
清單 2 演示了對 PUBLIC 模式的訪問被禁止。
清單 2. 禁止角色 user1 創建任何實體
postgres=> SET SESSION AUTHORIZATION postgres;SETpostgres=# REVOKE ALL PRIVILEGES ON SCHEMA PUBLIC FROM user1;REVOKEpostgres=# SET SESSION AUTHORIZATION user1;SETThe error message of "ERROR: permission denied for schema user1" means that this defensive measure works:postgres=> CREATE TABLE X;ERROR: permission denied for schema user1
訪問由其他用戶控制的對象
如下面清單 3 所示的攻擊媒介假設用戶可以訪問 PUBLIC 模式;例如,GRANT USAGE ON SCHEMA PUBLIC TO user1。它以下面的假設作為前提:
•所有用戶在默認情況下都有權訪問集群中的任何數據庫。
•假設集群允許用戶創建并操作 PUBLIC 模式中的所有實體。
•一個普通用戶帳戶有權訪問系統目錄。否則,用戶帳戶不能正常工作(固有的 PostgreSQL 服務器行為)。
清單 3. 收集有關表的信息
postgres=> SELECT * FROM user1.t2;ERROR: permission denied for relation t2postgres=> insert into user1.t2 values(10);ERROR: permission denied for relation t2postgres=>postgres=> \d List of relations Schema | Name | Type | Owner--------+------+-------+---------- user1 | t1 | table | postgres user1 | t2 | table | postgres(2 rows)postgres=> \d t? Table "user1.t1" Column | Type | Modifiers--------+---------+----------- i | integer | Table "user1.t2" Column | Type | Modifiers--------+---------+----------- i | integer |
盡管可能無法訪問表,但是用戶仍然可以收集有關表的信息。
清單 4 展示了用戶帳戶 user1 獲取了一組用戶帳戶及其各自的屬性。普通用戶自己無法訪問密碼。
清單 4. 獲取用戶帳戶的屬性
postgres=> select * from pg_user; usename | usesysid | usecreatedb | usesuper | usecatupd | passwd | valuntil | useconfig----------+----------+-------------+----------+-----------+----------+----------postgres | 10 | t | t | t | ******** | | user1 | 18770 | f | f | f | ******** | |(2 rows)
所有用戶默認情況下都能夠獲得集群的定義和模式。
清單 5 展示了一個可以獲得有關集群完整定義模式的信息的腳本,方法就是查詢系統目錄。系統目錄可以被超級用戶修改或解密,從而減輕了這一威脅。
清單 5. 提取集群范圍內的定義
#!/bin/bashpsql mydatabase << _eof_set search_path=public,information_schema,pg_catalog,pg_toast;\t\o list.txtSELECT n.nspname||'.'||c.relname as "Table Name"FROM pg_catalog.pg_class c JOIN pg_catalog.pg_roles r ON r.oid = c.relowner LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespaceWHERE c.relkind IN ('r','')ORDER BY 1;\q_eof_for i in $( cat list.txt ); do psql -c "\d $i"done
創建和訪問由用戶定義的函數
函數也分為可信的或不可信的。可信的 過程語言在數據庫上下文內執行指令,比如創建表、索引,添加或移除數據,等等。不可信的 過程語言(除了可信的特性外)也能夠影響真實的世界,比如列出目錄的內容,創建或刪除文件,調用系統進程,甚至是創建與其他主機的套接字連接。
清單 6. 向數據庫添加過程語言并恢復對 user1 的訪問
postgres=# create language plpgsql;CREATE LANGUAGEpostgres=# create language plperlu;CREATE LANGUAGEpostgres=# create language plperl;CREATE LANGUAGEpostgres=> SET SESSION AUTHORIZATION postgres;SETpostgres=# GRANT USAGE ON SCHEMA PUBLIC TO user1;GRANT
清單 7. 可信 vs. 不可信過程語言
postgres=# select lanname as language, lanpltrusted as trusted from pg_language; language | trusted----------+--------- internal | f c | f sql | t plperlu | f plperl | t(5 rows)
與表不同的是,普通用戶帳戶在調用某些用戶的函數時不需要特殊權限,即使是由超級用戶創建的函數也是如此。
清單 8. 調用超級用戶的函數
postgres=# SET SESSION AUTHORIZATION postgres;SETpostgres=# CREATE OR REPLACE FUNCTION public.f1 (postgres(# OUT x textpostgres(# ) ASpostgres-# $body$postgres$# select 'hello from f1'::text;postgres$# $body$postgres-# LANGUAGE SQL;CREATE FUNCTIONpostgres=# SET SESSION AUTHORIZATION user1;SETpostgres=>postgres=> SELECT * FROM f1; x----------------- hello from f1(1 row)
下面清單 9 中的函數由超級用戶通過 plperlu 創建。它返回目錄的內容;user1 可以調用這個函數。一個普通用戶可以同時調用可信函數和不可信函數。要應對這種威脅,最好的方法是通過撤銷權限來拒絕對函數的訪問。
清單 9. 函數被未授權的用戶利用
postgres=> SET SESSION AUTHORIZATION postgres;SETpostgres=# CREATE OR REPLACE FUNCTION public.f2 (postgres(# OUT x textpostgres(# ) ASpostgres-# $body$postgres$# # output the root directory contents into standard outputpostgres$# # notice the use of the single back tickspostgres$# $a = `ls -l / 2>/dev/null`;postgres$# $message = "\nHere is the directory listing\n".$a;postgres$# return $message;postgres$# $body$postgres-# LANGUAGE PLPERLU;CREATE FUNCTIONpostgres=# SET SESSION AUTHORIZATION user1;SETpostgres=> SELECT * FROM f2; x---------------------------------------------------------------------------- Here is the directory listing total 120 drwxr-xr-x 2 root root 4096 Aug 29 07:03 bin drwxr-xr-x 3 root root 4096 Oct 11 05:17 boot drwxr-xr-x 3 root root 4096 Nov 26 2006 build lrwxrwxrwx 1 root root 11 Aug 22 2006 cdrom -> media/cdrom drwxr-xr-x 15 root root 14960 Oct 12 07:35 dev drwxr-xr-x 118 root root 8192 Oct 12 07:36 etc(1 row)
清單 10. 針對 user1 和組 PUBLIC 提供保護
postgres=# SET SESSION AUTHORIZATION postgres;SETpostgres=# REVOKE ALL ON FUNCTION f2 FROM user1, GROUP PUBLIC;REVOKEpostgres=# SET SESSION AUTHORIZATION user1;SETpostgres=> SELECT * FROM f2;ERROR: permission denied for function f2postgres=>
清單 11 展示了信息收集。
清單 11. 獲取函數的源代碼
postgres=> SET SESSION AUTHORIZATION user1;SETpostgres=> select prosrc as "function f3" from pg_proc where proname='f3'; function f3---------------# output the root directory contents into standard output# notice the use of the single back ticks $a = `ls -l / 2>/dev/null`; $message = "\nHere is the directory listing\n".$a; return $message;(1 row)
#p#
要隱藏這些源代碼:
•使用原生語言環境(C、Perl、Python 等)將函數編寫為一個模塊并存儲到主機的硬盤中。隨后在 PostgreSQL 中創建一個抽象的用戶定義函數來調用該模塊。
•考慮在一個表中編寫源代碼并在需要時動態創建函數。
•在集群的另一個數據庫中編寫您的用戶定義函數,該函數將由另一個已授權用戶帳戶通過 dblink 模塊調用。
使用 security definer
security definer 使用創建函數的用戶所擁有的特權執行函數。因此,用戶可以訪問在平常狀態下不可用的表。
例如,如清單 12 所示,一個包含兩個列的表在模式 Postgres 中由超級用戶 postgres 創建。普通用戶 user1 將使用 security definer 參數調用該函數并根據輸入值獲得一個值。
清單 12. 創建一個表和函數
postgres=# SET SESSION AUTHORIZATION postgres;SETpostgres=# CREATE TABLE postgres.t4(x serial,y numeric);NOTICE: CREATE TABLE will create implicit sequence "t4_x_seq" for serial column "t4.x"CREATE TABLEpostgres=# INSERT INTO postgres.t4(y) VALUES (random::numeric(4,3));INSERT 0 1postgres=# INSERT INTO postgres.t4(y) VALUES (random::numeric(4,3));INSERT 0 1postgres=# INSERT INTO postgres.t4(y) VALUES (random::numeric(4,3));INSERT 0 1postgres=# INSERT INTO postgres.t4(y) VALUES (random::numeric(4,3));INSERT 0 1postgres=# INSERT INTO postgres.t4(y) VALUES (random::numeric(4,3));INSERT 0 1postgres=# CREATE OR REPLACE FUNCTION public.f4 (postgres(# IN a int,postgres(# OUT b numericpostgres(# ) RETURNS SETOF numeric ASpostgres-# $body$postgres$# select y from postgres.t4 where x=$1 limit 1;postgres$# $body$postgres-# LANGUAGE SQL SECURITY DEFINER;CREATE FUNCTION
清單 13 表明用戶帳戶 user1 現在可以訪問所需的信息。
清單 13. 未授權角色通過一個函數調用訪問表
postgres=# SET SESSION AUTHORIZATION user1;SETpostgres=> SELECT b as "my first record" FROM f4(1); my first record----------------- 0.379(1 row)postgres=> SELECT b as "my second record" FROM f4(2); my second record------------------ 0.200(1 row) 破解 PostgreSQL 密碼
有效的密碼管理是保證 DBMS 安全性的關鍵。DBA 的職責就是實施一項獲得認可的密碼策略。密碼應當由隨機選擇的字符組成,這些字符不具備可識別的模式。常見實踐表明密碼至少應有 6 個字符并且需要經常更換。
PostgreSQL 用戶帳戶和密碼
PostgreSQL 用戶帳戶安全策略主要與創建和管理用戶帳戶的 SQL 命令有關:
•CREATE ROLE
•ALTER ROLE
•DROP ROLE
下面的 SQL 語句屬于比較舊的用戶帳戶管理(盡管有效,您仍然應當使用較新的技術管理用戶角色):
•CREATE GROUP
•ALTER GROUP
•DROP GROUP
•CREATE USER
•ALTER USER
•DROP USER
密碼可以以加密或非加密形式保存。非加密 密碼以明文形式存儲,并且可以被超級用戶讀取。密碼的加密 包括生成并存儲其 MD5 散列,MD5 散列是無法讀取的。要在登錄時對密碼進行驗證,需要對密碼進行散列化并將其與已經存儲在數據集群中的密碼進行比較。
下面是一些創建和管理密碼的示例方法:
•未使用密碼創建帳戶: CREATE ROLE user1 WITH LOGIN;
•使用未加密密碼創建帳戶: CREATE ROLE roger WITH LOGIN UNENCRYPTED PASSWORD '123'
•修改帳戶并分配一個加密過的密碼: ALTER ROLE user1 WITH ENCRYPTED PASSWORD '123'
由超級用戶對目錄表 pg_shadow 執行一個 SQL 查詢,結果返回用戶帳戶名及其密碼。清單 4 展示了代碼。
清單 14. 從目錄獲得用戶的密碼
postgres=# select usename as useraccount,passwd as "password" from pg_shadow wherelength(passwd)>1 order by usename; useraccount | password-------------+------------------------------------- user1 | md5173ca5050c91b538b6bf1f685b262b35 roger | 123(2 rows)
清單 15 展示了如何為使用密碼 123 的 user1 生成 MD5 散列。
清單 15. 生成 MD5 密碼
postgres=# select 'md5'||md5('123user1') as "my own generated hash", passwd as "stored hash for user1" from pg_shadow where usename='user1'; my own generated hash | stored hash for user1-------------------------------------+------------------------------------- md5173ca5050c91b538b6bf1f685b262b35 | md5173ca5050c91b538b6bf1f685b262b35(1 row)
準備好再受一次驚嚇了嗎?PostgreSQL 幾乎沒有提供任何機制以實施可靠的密碼策略。
可能的安全局限性包括:
•超級用戶無法對將用于密碼的字符限制最小字符數。
•盡管針對如何保存密碼(未加密的或已加密的 MD5 散列)的配置設置提供了一個默認參數,超級用戶無法強制用戶使用特定的存儲方法。
•不存在對用戶帳戶施加生命周期的機制。
•在集群的客戶機身份驗證配置文件 pg_hba.conf 中,當連接方法不是 PASSWORD 或 MD5 時,控制用戶帳戶的有效生命周期的機制將變得無關緊要。
•由 ALTER ROLE 語句修改的用戶運行時參數,以及由超級用戶或文件 postgresql.conf 中的默認配置設置的參數,都可以由用戶帳戶的所有者隨意修改。
•重命名一個用戶帳戶將清除該帳戶的密碼,如果已被加密的話。
•無法跟蹤誰對用戶帳戶做了修改或何時做了修改。
一個具有精心系統目錄設計的強有力的架構可以為隨時保持警惕的 DBA 帶來回報。
由于存在各種各樣的危害,因此有關用戶帳戶和密碼的安全局限性需要用另一篇文章單獨詳細介紹。
#p#
破解密碼
實施一個強類型的密碼是值得的,但是沒有辦法去判斷它的強度,除非有人破解它。破解實用程序主要基于兩種方法,如下所示。
蠻力攻擊(Brute force)
針對散列的系統化測試。它以一些字母為起點,隨著攻擊的繼續不斷增加字母的長度。這種方法被建議用于測試較短的密碼。
字典攻擊(Dictionary attack)
使 用一種社會工程(social-engineering)方法。破解程序使用一個單詞字典作為起點。此后,不斷組合這些單詞并針對捕捉到的散列進行測試。 這種攻擊利用了一種錯誤的觀念,即由有助于記憶的字符串和字符組合組成的長字符串要比由隨機字符組成的稍微短一些的字符串更加安全。
根據密碼的強度以及使用的硬件,用于解密的時間可能從幾秒到幾個月不等。
DBA 對識別長度小于 6 個字符的密碼比較感興趣。
命令行實用工具 MDCrack 使用蠻力攻擊法測試密碼。這個 Windows 二進制工具在 Linux under Wine 上也可以很好地工作。
輸入 wine MDCrack-sse.exe --help 將返回配置參數(switch)。其中一些如下面所示:
Usage: MDCrack [options...] --test-hash|hash MDCrack [options...] --bench[=PASS] MDCrack [options...] --resume[=FILENAME]|--delete[=FILENAME] MDCrack [options...] --help|--about
最簡單的命令行調用是 wine MDCrack-sse.exe --algorithm=MD5 --append=$USERNAME $MD5_HASH,其中 $USERNAME 為用戶名,而 $MD5_HASH 是 pg_shadow 目錄文件中的 MD5 散列。
如下所示,MDCrack 可以在會話模式下運行,因此您可以停止一個解密操作并在稍后繼續執行。
清單 16. 在會話模式下運行的 MDCrack
# start sessionwine MDCrack-sse.exe --algorithm=MD5 --append=$USERNAME $MD5_HASH\ --session=mysessionfile.txt# resume using the last session modewine MDCrack-sse.exe --algorithm=MD5 --append=$USERNAME $MD5_HASH\ --resume=mysessionfile.txt
默 認字符集為 abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ。如果要測試的密碼包 含了不屬于默認字符集的字符,那么您可以中止進程。您可以將其修改為由字母和數字字符組成的任意組合。例如,您可能希望包含控制字符和標點符號。
調整字符集是在命令行中完成的。變量 $CHARSET 表示將要使用的實際字符集:
wine MDCrack-sse.exe --algorithm=MD5 --append=$USERNAME $MD5_HASH --charset=$CHARSET
下面的示例將修改 Postgres 密碼 123。如果忽略前三個字符,那么將得到 MD5 散列值 md5173ca5050c91b538b6bf1f685b262b35。您可以通過以下調用確定密碼(提示:對字符串 Collision found 執行 grep 命令)。這次解密用了大約 0.32 秒:
wine MDCrack-sse.exe --algorithm=MD5 --append=user1 173ca5050c91b538b6bf1f685b262b35\| grep "Collision found"
清單 17 演示了在系統目錄 pg_shadow 中解密密碼。
清單 17. 解密密碼
wine MDCrack-sse.exe --algorithm=MD5 --append=user1 \`psql -t -c "select substring(passwd,4) from pg_shadow where usename='user1';"` \| grep "Collision found" 身份驗證模型
您已經知道了哪些部分會出現問題,現在讓我們看看采取哪些措施來糾正錯誤。身份驗證是一個龐大的主題,因此這里只涉及一些基本的知識。
在 “身份驗證” 這一龐大主題下,可以用許多種方法控制對 Postgres 集群的訪問:
•UNIX 域套接字
•Ident 服務器身份驗證
•LDAP 服務器身份驗證
•PAM
•Kerberos
•SSL
UNIX 域套接字
一個 UNIX 域套接字就是一個雙向通信管道,它在許多方面與文件相似。服務器創建域套接字,域套接字等待客戶機通過文件系統打開文件。一個典型的 PostgreSQL 域套接字如下所示。
#p#
清單 18. 典型的域套接字
robert@wolf:~$ ls -la /tmp|grep PGSQLsrwxrwxrwx 1 robert robert 0 2007-10-15 12:47 .s.PGSQL.5432-rw------- 1 robert robert 33 2007-10-15 12:47 .s.PGSQL.5432.lock
注意,端口號被附加到文件名的末尾。將服務器重新配置為使用一個不同的 TCP/IP 端口也將改變域套接字的名稱。
postgresql.conf 配置文件中的三個參數將控制對域套接字的權限:
•unix_socket_directory(文件 PATH)
•unix_socket_group(用戶組)
•unix_socket_permissions(默認為 0777)
域套接字的位置因 Linux 發行版而異:
•PostgreSQL 源代碼安裝并將套接字放到 /tmp 目錄。
•BSD 將套接字放到 /tmp 目錄。
•RedHat 衍生系統將套接字放到 /tmp 目錄。
•Debian 衍生系統將套接字放到 /var/run/postgresql 且只具有對 postgresqlaccount 的權限。
域套接字有一些比較特別的方面。考慮下面這個例子。
在 robert(超級用戶)的主目錄中創建一個集群,并對可信性進行了驗證。但是,在服務器啟動時,域套接字的權限只允許除 robert 以外的用戶登錄。用戶 robert 使用 TCP 登錄,但是被域套接字拒絕。然而,robert 在對 nobody 執行 sudo 后,他就可以通過域套接字登錄。
這個例子展示了文件權限的多樣性,從而減輕由于駭客成為超級用戶而引起的破壞。
清單 19. 權限
robert@wolf:~$ initdb -A trust -U postgres ~/datarobert@wolf:~$ pg_ctl -D ~/data/ -l ~/logfile.txt \-o "-c unix_socket_permissions=007 -c unix_socket_directory=/tmp" startserver startingrobert@wolf:~$ psql -h localhost -U postgres -c "select 'superuser:this works' as msg" msg---------------------- superuser:this works(1 row)robert@wolf:~$ psql -h /tmp -U postgres -c "select 'superuser:this fails' as msg"psql: could not connect to server: Permission denied Is the server running locally and accepting connections on Unix domain socket "/tmp/.s.PGSQL.5432"?robert@wolf:~$ sudo su nobody[sudo] password for robert:$ psql -h localhost -U postgres -c "select 'nobody:this works' as msg" msg------------------- nobody:this works(1 row)$ psql -h /tmp -U postgres -c "select 'nobody:this still works' as msg" msg------------------------- nobody:this still works(1 row)
#p#
Ident
Ident 服務器回答了一個簡單的問題:哪些用戶從您的端口 X 發起連接并連接到我的端口 Y? 在 PostgreSQL 服務器的環境下,它將通知正在嘗試登錄的用戶帳戶的 Identity 所在的 DBMS。PostgreSQL 隨后獲得這個問題的答案,并根據由 DBA 在相應配置文件中配置的規則集允許或拒絕登錄。
PostgreSQL Ident 服務器身份驗證機制的工作原理是使用主機自己的 Ident 服務器,將 PostgreSQL 用戶帳戶映射到 UNIX 用戶帳戶。
下 面的例子假設所有 UNIX 用戶帳戶都已經被映射到 PostgreSQL 中,能夠登錄到任何數據庫,前提是它們在 PostgreSQL 中使用相同的帳戶名。如果 UNIX 用戶名在 PostgreSQL 服務器中不存在對應的用戶名,或者如果嘗試使用另一個 用戶帳戶名登錄,那么登錄將失敗。
假設您已經通過 SSH 連接到主機:ssh -l robert wolf。
清單 20. 失敗的和成功的登錄
robert@wolf:~$ psql -U robert robertWelcome to psql 8.2.4, the PostgreSQL interactive terminal.Type: \copyright for distribution terms \h for help with SQL commands \? for help with psql commands \g or terminate with semicolon to execute query \q to quitrobert@wolf:~$ psql -U postgres robertpsql: FATAL: Ident authentication failed for user "postgres"-- This works, su to become the UNIX user account postgres
PostgreSQL 使用兩個文件管理并控制已通過 Ident 服務器身份驗證的用戶的所有登錄會話:
pg_hba.conf
通過一個文件中定義的記錄控制訪問。
pg_Ident.conf
當 Ident 服務被用作用戶帳戶的認證者(authenticator)時發揮作用。比如,METHOD 在 pg_hba.conf 文件中被識別為 Ident。
清單 21. 簡單配置示例
Example 1: A LOCALHOST connection enforces unix account robert to access database robert exclusively. There is no authentication on UNIX domain sockets.(pg_hba.conf)# TYPE DATABASE USER CIDR-ADDRESS METHOD OPTION host all all 127.0.0.1/32 Ident mymap local all all trust(pg_Ident.conf)# MAPNAME Ident-USERNAME PG-USERNAME mymap robert robertExample 2: A domain socket connection enforces unix account robert to access any database as pg account robert; unix account postgres can access any database as user robert.(pg_hba.conf)# TYPE DATABASE USER CIDR-ADDRESS METHOD OPTION local all all Ident mymap host all all 127.0.0.1/32 trust(pg_Ident.conf)# MAPNAME Ident-USERNAME PG-USERNAME mymap robert robert mymap postgres robertExample 3: A domain socket connection enforces that unix account can connect to any database with its postgres database namesake using thekeyword "sameuser". pg_Ident.conf is not necessary here. Local host connections via TCP-IP are rejected.(pg_hba.conf)# TYPE DATABASE USER CIDR-ADDRESS METHOD OPTION local template0,template1 all Ident sameuser host all all 127.0.0.1/32 rejectEx4: (all users can connect with their own user names only to the databases postgres and robert)(pg_hba.conf)# TYPE DATABASE USER CIDR-ADDRESS METHOD OPTION local template0,template1 all Ident sameuser
牢記下面的注意事項:
•配置修改在您重新加載文件后立即生效,比如重新加載 pg_ctl -D mycluster。
•不同的配置設置會引起奇怪的行為。在日志中查看失敗的配置消息。
•當機器本身被認為是安全的時,將設計并實現 Ident。任何試圖進行身份驗證的遠程服務器都應當被認為是可疑對象。
•Ident 服務器只用于對本地主機連接進行身份驗證。
數據加密
有許多種方式會使您不經意地將自己暴露給 intranet 內部的駭客。
讓我們來執行一次嗅探(sniff)。假設您在自己的本地主機 192.168.2.64 上執行以下命令:tcpdump -i eth0 -X -s 3000 host 192.168.2.100 and port 5432。
在一個遠程主機 192.168.2.100 上,您連接到您的本地主機的 PostgreSQL 服務器,后者已經在偵聽端口 5432:psql -h 192.168.2.64 -p 5432 -U postgres postgres。注意修改您的超級用戶帳戶 postgres 的密碼:ALTER USER postgres WITH ENCRYPTED PASSWORD 'my_new_password';。
清單 22. 在被嗅探的數據轉儲中識別密碼
16:39:17.323806 IP wolf.56336 > laptop.postgresql: P 598:666(68) ack 470 win 3068
#p#
SSH 隧道使用端口轉發
IP 轉發是一項隧道(tunneling)技術,它可以將 Internet 包從一臺主機轉發到另一臺主機。它允許您的 PostgreSQL 客戶機,比如 psql、pgadmin 甚至 openoffice,通過一個 SSH 連接與遠程 Postgres 服務器建立聯系。
考慮下面的問題:
•如果遠程 PostgreSQL 服務器上不存在 psql 客戶機,會發生什么?
•如果需要在您的工作站和遠程主機之間上傳或下載數據,會發生什么?
•如果需要使用數據庫客戶機來執行 psql 客戶機無法很好地執行或根本無法執行的任務,該怎么做?
•如何對網絡使用隧道,從而使您的團隊能夠遠程連接到防火墻后面的數據庫?
這個例子將一個客戶機(本地主機)連接到一臺遠程主機(192.168.2.100)。在工作站的端口 10000 創建了一個代理連接。客戶機連接到端口 10000 后,被轉發到遠程主機的 PostgreSQL 服務器,后者正在偵聽端口 5432:ssh -L 10000:localhost:5432 192.168.2.100。
添加 -g 參數(switch)允許其他主機利用您的轉發連接,這使得連接成為了專門用于 Postgres 連接的即時虛擬專有網(VPN):ssh -g -L 10000:localhost:5432 192.168.2.100.
有關隧道的一些注意事項:
•數據庫客戶機和服務器都被認為它們正在與各自的本地主機通信。
•注意要配置文件 pg_hba.conf 以使用 TCP/IP 為本地主機連接設置正確的身份驗證。
•1024 以內的端口全部由根用戶控制。
•SSH 會話需要在 PostgreSQL/SSH 服務器上有一個已有的用戶帳戶。
由 SSL 加密的會話
PostgreSQL 中的加密會話要求服務器通過 --with-openssl 參數進行編譯。Linux 發行版二進制文件提供了這個功能。諸如 psql 和 pgadmin 之類的客戶機也具有這類必需的功能。
可以使用 pg_config 命令行工具對服務器進行檢驗,如下所示。
pg_config --configure
針對加密會話配置 PostgreSQL 服務器:
1.使用 OpenSSL 命令行工具 openssl 創建一個自簽名的服務器密匙(server.key)和證書(server.crt)。
1.創建服務器密匙:openssl genrsa -des3 -out server.key 1024。
2.刪除密碼 openssl rsa -in server.key -out server.key。
3.為服務器創建一個自簽名的證書:openssl req -new -key server.key -x509 -out server.crt。
2.將 server.key 和 server.crt 這兩個文件安裝到數據集群的目錄中。
3.編輯 postgresql.conf 文件并設置指定的對:ssl = on。
4.重啟服務器。
清單 23. 成功的 SSL 加密會話連接
robert@wolf:~$ psql -h 192.168.2.100 -U robertWelcome to psql 8.2.4, the PostgreSQL interactive terminal.Type: \copyright for distribution terms \h for help with SQL commands \? for help with psql commands \g or terminate with semicolon to execute query \q to quitSSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)robert=#
服務器將始終針對已加密的會話請求測試連接。然而,通過編輯身份驗證文件 pg_hba.conf,您可以控制服務器的行為。在客戶機端,可以通過定義環境變量 PGSSLMODE 來控制客戶機(psql)的默認行為:是否使用加密的會話。
共有 6 種模式(其中兩種新模式特別針對 V8.4)。
模式 描述
disable 將只嘗試未加密的 SSL 連接。
allow 首先嘗試未加密的連接,如果不成功,則嘗試 SSL 連接。
prefer 與 allow 模式相反;首先嘗試 SSL 連接,然后嘗試未加密連接。
require 客戶機只嘗試已加密的 SSL 連接。
verify-ca SSL 連接,并且具有由可信 CA 簽發的有效客戶機證書。
Verify-full SSL 連接,具有由可信 CA 簽發的有效客戶機證書,并且服務器主機名匹配證書的主機名。
例如:導出 PGSSLMODE=prefer.
SSL 證書
SSL 身份驗證是指客戶機和服務器交換由具有可靠憑證的第三方簽發的證書。這個第三方被稱為證書權威(CA)。如果客戶機或服務器不能從另一方接收到合法的證書,那么連接將被拒絕。
盡管涉及許多細節,但是在 PostgreSQL 上使用 SSL 證書設置身份驗證非常簡單:
1.編輯 postgresql.conf,ssl=on。 服務器端身份驗證要求下面的文件必須位于其數據集群中:
•server.key
•server.crt(必須由 CA 簽發)
•root.crt(檢驗客戶機身份驗證)
•root.crl(證書撤銷列表,可選)
文件 root.crt 包含一個經過檢驗的證書列表。其中應該包含可用于您的特定發行版的所有證書,您也可以向該文件中添加證書。
文件 root.crl 類似于 root.crt,因為它包含一組經過 CA 簽名的證書。然而,這些證書屬于已被撤銷了連接權的客戶機。一個空的 root.crl 文件不會影響身份驗證過程。
客戶端身份驗證要求下面的文件必須位于客戶機的主目錄 ~/.postgresql 中:
•postgresql.key
•postgresql.crt
•root.crt(檢驗服務器身份驗證)
•root.crl(證書撤銷列表,可選)
和服務器的 root.crt 一樣,客戶機的 root.crt 文件包含了由一個可信的第三方 CA 簽名的服務器證書的列表。最后一個文件 root.crl 是可選的,用于撤銷服務器證書。
要獲取證書,需要客戶機和服務器都向 CA 提交了證書請求 client.csr 和 server.csr。證書只有在生成了它們的密匙后才能被創建,如下所示。
openssl req -new -newkey rsa:1024 -nodes -keyout client.key -out client.csropenssl req -new -newkey rsa:1024 -nodes -keyout server.key -out server.csr
可以使用多種方法執行 openssl 實用工具來獲得證書。例如,您可以對它們施加一個生命周期,或者使用自簽名證書來生成它們,這樣就不需要涉及到 CA。
2.您現在可以使用三種方法生成客戶機和服務器證書。您可以:
•獲得由可信 CA 簽名的 client.csr 和 server.csr。
•通過使用 openssl perl 實用工具 CA.pl 成為 CA。
•創建自簽名證書并分別將它們添加到服務器和客戶機的 root.crt 文件中。
3.下面是一組用于 CA.pl 的命令,它們被進行了縮減。查看 CA.pl 手冊頁獲得有關證書請求的更多信息。
•CA.pl -newca(創建新 CA)
•CA.pl -newreq(使用私匙創建一個證書請求)
•CA.pl -signreq (使用您創建的 CA 對證書請求簽名)
對于堅持使用純開源技術的人,可以在 http://www.cacert.org 找到 “免費” 的證書。
清單 24. 一個示例證書
-----BEGIN CERTIFICATE-----MIIC9TCCAl6gAwIBAgIJAMuhpY+o4QR+MA0GCSqGSIb3DQEBBQUAMFsxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFDASBgNVBAMTC0NvbW1vbiBOYW1lMB4XDTA3MDIxMjEyMjExNVoXDTA3MDMxNDEyMjExNVowWzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAxMLQ29tbW9uIE5hbWUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKA4nX/eBKsPJI1DmtH2wdJE9uZf+IRMUWYrAEDL4F6NEuo2+BsIoOBKS/rrV77Itet9kduJCQ6k/z2ouAVb4muXpJALDjJpYBXt9wqZf+2p1n9dqDw1rCWBjXIdhOcA3DDvu0Ig1FUfm8GS97evxM5IJBECRnK/5JZroXCRSHcpAgMBAAGjgcAwgb0wHQYDVR0OBBYEFElEWNUCV+61itXp86czrDe35vjrMIGNBgNVHSMEgYUwgYKAFElEWNUCV+61itXp86czrDe35vjroV+kXTBbMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDEwtDb21tb24gTmFtZYIJAMuhpY+o4QR+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAaFzbUmXcWVzqaVeEpZkNwF/eVh110qIUUxXGdeKZGNXIyK67GCUYSG/IFkZ/hrGLeqElLrdmU0mHd2Enq2IuvhxnsOVTTickjKospJvlHPYSumkXx0Xpzey9PhjLh1chpxNGTATKb8ET8YZvBRrDHl/EMPIjLd62iSR/ugFe8go=-----END CERTIFICATE-----
4.假設您已經生成了自簽名證書,將它們復制到正確的位置并編輯 root.crt。 客戶機證書被保存在服務器的 root.crt 中,而服務器的證書被保存在客戶機的 root.crt 中。
5.重啟服務器后監視日志消息,確定一切配置正常。
服務器的默認行為將仍然使用加密。這可以通過編輯 postgresql.conf 中的名稱對 ssl_ciphers='NULL' 并重啟服務器禁用。慎重考慮您的決定:將 ssl_ciphers 設置為 NULL 就可以禁用加密。
結束語
在 本文中,您了解了有關 PostgresSQL 數據庫服務器保護的基本知識。有關這個話題還涉及到許多內容,但是一篇文章所能介紹的內容是有限的。目前有關 PostgreSQL 的介紹還不夠豐富。也許,借助您的一點幫助,我們可以更深入地了解 PostgreSQL 安全性。
【編輯推薦】