PostgreSQL 原生支援使用SSL連線,以使用TLS協定加密客戶端/伺服器通訊,以提高安全性。有關伺服器端SSL功能的詳細信息,請參閱第 18.9 節。
libpq 會讀取系統範圍的 OpenSSL 配置文件。 預設情況下,此檔案名為 openssl.cnf
,位於 openssl version -d
報告的目錄中。 可以透過將環境變數 OPENSSL_CONF
設定為所需的配置檔案名稱來覆蓋此預設值。
預設情況下,PostgreSQL 不會執行任何伺服器憑證的驗證。 這表示有可能欺騙伺服器身分(例如,透過修改 DNS 記錄或接管伺服器 IP 位址),而用戶端卻不知道。 為了防止欺騙,用戶端必須能夠透過信任鏈驗證伺服器的身分。 信任鏈的建立方式是在一部電腦上放置根(自簽署)憑證授權單位(CA)憑證,而在另一部電腦上放置由根憑證簽署的葉憑證。 也可以使用由根憑證簽署並簽署葉憑證的“中繼”憑證。
若要允許用戶端驗證伺服器的身分,請在用戶端上放置根憑證,並在伺服器上放置由根憑證簽署的葉憑證。 若要允許伺服器驗證用戶端的身分,請在伺服器上放置根憑證,並在用戶端上放置由根憑證簽署的葉憑證。 也可以使用一個或多個中繼憑證(通常與葉憑證一起儲存)將葉憑證鏈結到根憑證。
建立信任鏈後,用戶端有兩種方法可以驗證伺服器傳送的葉憑證。 如果參數 sslmode
設定為 verify-ca
,則 libpq 會檢查憑證鏈,直到儲存在用戶端上的根憑證,以驗證伺服器是否值得信任。 如果 sslmode
設定為 verify-full
,則 libpq 還會驗證伺服器主機名稱是否與儲存在伺服器憑證中的名稱相符。 如果無法驗證伺服器憑證,則 SSL 連線將失敗。 在大多數對安全性敏感的環境中,建議使用 verify-full
。
在 verify-full
模式下,主機名稱會與憑證的主體別名 (Subject Alternative Name, SAN) 屬性進行比對,如果沒有 dNSName
類型的 SAN,則會與通用名稱 (Common Name) 屬性進行比對。 如果憑證的名稱屬性以星號 (*
) 開頭,則星號將被視為萬用字元,它將比對除了點 (.
) 之外的所有字元。 這表示憑證將不比對子網域。 如果使用 IP 位址而不是主機名稱建立連線,則會比對 iPAddress
或 dNSName
類型的 SAN,來驗證 IP 位址(無需進行任何 DNS 查詢)。 如果沒有 iPAddress
SAN 且沒有比對的 dNSName
SAN,則主機 IP 位址會與通用名稱屬性比對。
為了與舊版 PostgreSQL 保持相容性,主機 IP 位址的驗證方式與 RFC 6125 不同。 主機 IP 位址始終與 dNSName
SAN 以及 iPAddress
SAN 進行比對,如果不存在相關 SAN,則可以與通用名稱屬性進行比對。
若要允許伺服器憑證驗證,必須將一個或多個根憑證放置在使用者主目錄中的 ~/.postgresql/root.crt
檔案中。 (在 Microsoft Windows 上,該檔案名為 %APPDATA%\postgresql\root.crt
。)如果需要將伺服器傳送的憑證鏈鏈結到儲存在用戶端上的根憑證,也應將中繼憑證新增到該檔案中。
如果存在檔案 ~/.postgresql/root.crl
(在 Microsoft Windows 上為 %APPDATA%\postgresql\root.crl
),也會檢查憑證撤銷清單 (Certificate Revocation List, CRL) 項目。
根憑證檔案和 CRL 的位置可以透過設定連線參數 sslrootcert
和 sslcrl
,或環境變數 PGSSLROOTCERT
和 PGSSLCRL
來變更。sslcrldir
或環境變數 PGSSLCRLDIR
也可以用來指定包含 CRL 檔案的目錄。
為了與舊版的 PostgreSQL 相容,如果存在根 CA 檔案,sslmode
=require
的行為將與 verify-ca
相同,這表示伺服器憑證會針對 CA 進行驗證。不鼓勵依賴此行為,需要憑證驗證的應用程式應始終使用 verify-ca
或 verify-full
。
如果伺服器嘗試透過請求用戶端的葉憑證來驗證用戶端的身份,libpq 將會傳送儲存在使用者主目錄中的 ~/.postgresql/postgresql.crt
檔案中的憑證。這些憑證必須鏈結到伺服器信任的根憑證。還必須存在一個相符的私鑰檔案 ~/.postgresql/postgresql.key
。在 Microsoft Windows 上,這些檔案的名稱分別為 %APPDATA%\postgresql\postgresql.crt
和 %APPDATA%\postgresql\postgresql.key
。憑證和金鑰檔案的位置可以被連線參數 sslcert
和 sslkey
,或環境變數 PGSSLCERT
和 PGSSLKEY
覆寫。
在 Unix 系統上,私鑰檔案的權限必須禁止任何對 world 或 group 的存取;可以透過執行類似 chmod 0600 ~/.postgresql/postgresql.key
的指令來實現。或者,該檔案可以由 root 擁有並且具有群組讀取權限(即 0640
權限)。此設定適用於作業系統管理憑證和金鑰檔案的安裝。然後,應該使 libpq 的使用者成為可以存取這些憑證和金鑰檔案的群組的成員。(在 Microsoft Windows 上,沒有檔案權限檢查,因為 %APPDATA%\postgresql
目錄被認為是安全的。)
postgresql.crt
中的第一個憑證必須是用戶端的憑證,因為它必須與用戶端的私鑰相符。“中繼”憑證可以選擇性地附加到檔案中 - 這樣做可以避免在伺服器上儲存中繼憑證(ssl_ca_file)。
憑證和金鑰可以是 PEM 或 ASN.1 DER 格式。
金鑰可以儲存在明文中,或者使用 OpenSSL 支援的任何演算法(例如 AES-128)透過密碼加密。如果金鑰以加密方式儲存,則可以在 sslpassword 連線選項中提供密碼。如果提供了加密金鑰並且缺少 sslpassword
選項或該選項為空白,如果 TTY 可用,OpenSSL 將會以 Enter PEM pass phrase:
提示互動式地提示輸入密碼。應用程式可以透過提供它們自己的金鑰密碼回呼來覆寫用戶端憑證提示和 sslpassword
參數的處理;請參閱 PQsetSSLKeyPassHook_OpenSSL
。
有關建立憑證的說明,請參閱 第 18.9.5 節。
sslmode
參數的不同值提供不同級別的保護。 SSL 可以針對三種類型的攻擊提供保護
如果第三方可以檢查用戶端和伺服器之間的網路流量,則它可以讀取連線資訊(包括使用者名稱和密碼)和傳遞的資料。SSL使用加密來防止這種情況。
如果第三方可以在用戶端和伺服器之間傳遞資料時修改資料,則它可以假裝成伺服器,因此可以查看和修改資料即使它已加密。然後,第三方可以將連線資訊和資料轉發到原始伺服器,從而無法檢測到此攻擊。常見的向量包括 DNS 投毒和位址劫持,藉此將用戶端導向到與預期不同的伺服器。還有幾種其他攻擊方法可以實現這一點。SSL使用憑證驗證來防止這種情況,透過驗證用戶端伺服器的身份。
如果第三方可以假裝成授權的用戶端,則它可以簡單地存取它不應該有權存取的資料。通常,這種情況可能透過不安全的密碼管理發生。SSL使用用戶端憑證來防止這種情況,透過確保只有有效憑證的持有者才能存取伺服器。
為了使連線被認為是 SSL 安全的,必須在用戶端和伺服器上都配置 SSL,然後才能建立連線。 如果僅在伺服器上進行配置,則用戶端可能最終會在知道伺服器需要高安全性之前傳送敏感資訊(例如,密碼)。在 libpq 中,可以透過將 sslmode
參數設定為 verify-full
或 verify-ca
,並為系統提供要驗證的根憑證來確保安全連線。這種類似於使用 https
網址進行加密的網頁瀏覽。
驗證伺服器後,用戶端可以傳遞敏感資料。 這表示在此之前,用戶端不需要知道是否會使用憑證進行驗證,因此可以安全地僅在伺服器配置中指定它。
所有SSL選項都會產生加密和金鑰交換形式的額外負擔,因此必須在效能和安全性之間進行權衡。表 32.1說明了不同的 sslmode
值所防禦的風險,以及它們關於安全性及額外負荷的描述。
表 32.1. SSL 模式說明
sslmode |
竊聽保護 | MITM中間人保護 | 聲明 |
---|---|---|---|
disable |
否 | 否 | 我不在乎安全性,而且我不想為加密支付額外負荷。 |
allow |
可能 | 否 | 我不在乎安全性,但是如果伺服器堅持,我願意支付加密的額外負荷。 |
prefer |
可能 | 否 | 我不在乎加密,但是如果伺服器支援,我希望支付加密的額外負荷。 |
require |
是 | 否 | 我希望我的資料被加密,並且我接受額外負荷。我相信網路會確保我始終連線到我想要的伺服器。 |
verify-ca |
是 | 取決於 CA 策略 | 我希望我的資料被加密,並且我接受因此產生的額外負擔。我希望確定我連線到一個我信任的伺服器。 |
verify-full |
是 | 是 | 我希望我的資料被加密,並且我接受因此產生的額外負擔。我希望確定我連線到一個我信任的伺服器,而且它是我所指定的伺服器。 |
verify-ca
和 verify-full
之間的差異取決於根憑證的策略。CA如果使用公開的CA,verify-ca
允許連線到一個由其他人向CA註冊的伺服器。在這種情況下,應始終使用 verify-full
。如果使用本地的CA,甚至是自我簽署憑證,使用 verify-ca
通常能提供足夠的保護。
sslmode
的預設值是 prefer
。如表格所示,從安全性的角度來看,這毫無意義,並且如果可能的話,只會帶來效能上的額外負擔。它僅作為向後相容的預設值提供,不建議在安全的部署中使用。
表 32.2 總結了與用戶端 SSL 設定相關的檔案。
表 32.2. Libpq/用戶端 SSL 檔案使用方式
檔案 | 內容 | 效果 |
---|---|---|
~/.postgresql/postgresql.crt |
用戶端憑證 | 傳送至伺服器 |
~/.postgresql/postgresql.key |
用戶端私鑰 | 證明由擁有者傳送的用戶端憑證;不表示憑證擁有者是值得信任的 |
~/.postgresql/root.crt |
受信任的憑證授權單位 | 檢查伺服器憑證是否由受信任的憑證授權單位簽署 |
~/.postgresql/root.crl |
由憑證授權單位撤銷的憑證 | 伺服器憑證不得在此列表中 |
如果您的應用程式初始化 libssl
和/或 libcrypto
函式庫,並且 libpq 是使用SSL支援建置的,您應該呼叫 PQinitOpenSSL
,以告知 libpq,libssl
和/或 libcrypto
函式庫已由您的應用程式初始化,因此 libpq 也將不會初始化這些函式庫。 但是,當使用 OpenSSL 1.1.0 或更高版本時,這是不必要的,因為重複的初始化不再存在問題。
PQinitOpenSSL
#允許應用程式選擇要初始化哪些安全函式庫。
void PQinitOpenSSL(int do_ssl, int do_crypto);
當 do_ssl
為非零值時,libpq 將在首次開啟資料庫連線之前初始化 OpenSSL 函式庫。 當 do_crypto
為非零值時,將初始化 libcrypto
函式庫。 預設情況下 (如果未呼叫 PQinitOpenSSL
),則會初始化這兩個函式庫。 如果未編譯 SSL 支援,則此函式存在但不起作用。
如果您的應用程式使用並初始化 OpenSSL 或其底層的 libcrypto
函式庫,您必須在首次開啟資料庫連線之前,使用適當的參數 (一個或多個) 的零值來呼叫此函式。 此外,請確保您已在開啟資料庫連線之前完成該初始化。
PQinitSSL
#允許應用程式選擇要初始化哪些安全函式庫。
void PQinitSSL(int do_ssl);
此函式等效於 PQinitOpenSSL(do_ssl, do_ssl)
。 它足以滿足同時初始化 OpenSSL 和 libcrypto
或兩者都不初始化的應用程式的需求。
PQinitSSL
自 PostgreSQL 8.0 起就已存在,而 PQinitOpenSSL
是在 PostgreSQL 8.4 中新增的,因此對於需要與舊版本 libpq 一起使用的應用程式,PQinitSSL
可能更可取。
如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用此表單報告文件問題。