选择 打开 改范围 完整检索页
受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10
当前 PostgreSQL 版本不在支持生命周期内。
您可以参阅当前版本的对应页面,或其他在上面列出的活跃大版本。

33.18. SSL 支持 #

PostgreSQL 原生支持使用 SSL 连接来加密客户端与服务器之间的通信,以提高安全性。有关服务器端 SSL 功能的详细信息,请参见 Section 18.9

libpq读取系统范围的OpenSSL配置文件。默认情况下,这个文件被命名为openssl.cnf并且位于openssl version -d所报告的目录中。可以通过设置环境变量OPENSSL_CONF把这个默认值覆盖为想要的配置文件的名称。

33.18.1. 服务器证书的客户端验证 #

默认情况下,PostgreSQL将不会执行服务器证书的任何验证。这意味着可以在不被客户端知晓的情况下伪造服务器身份(例如通过修改一个 DNS 记录或者接管服务器的 IP 地址)。为了阻止哄骗,客户端必须能够通过一条信任链验证服务器的身份。信任链可以这样建立:在一台计算机上放置一个根(自签名的)证书机构(CA)的证书并且在另一台计算机上放置一个由根证书签发的叶子证书。还可以使用一种中间证书,它由根证书签发并且可以签发叶子证书。

要让客户端验证服务器的身份,请在客户端放置根证书,并在服务器上放置由该根证书签名的叶证书。要让服务器验证客户端的身份,请在服务器上放置根证书,并在客户端放置由该根证书签名的叶证书。也可以使用一个或多个中间证书(通常与叶证书存储在一起),将叶证书链接到根证书。

建立信任链后,客户端可以通过两种方式验证服务器发送的叶证书。如果参数 sslmode 设为 verify-ca,libpq 会沿证书链检查到存储在客户端上的根证书,以验证服务器是否可信。如果 sslmode 设为 verify-full,libpq 还会验证服务器主机名是否与服务器证书中存储的名称匹配。如果无法验证服务器证书,SSL 连接将失败。在大多数对安全敏感的环境中,建议使用 verify-full

verify-full 模式下,会将主机名与证书的主体替代名称属性匹配;如果不存在类型为 dNSName 的主体替代名称,则与通用名称属性匹配。如果证书的名称属性以星号(*)开头,该星号会被视为通配符,匹配点(.)以外的所有字符。这意味着该证书不会匹配子域。如果使用 IP 地址而不是主机名建立连接,则会匹配该 IP 地址(不执行任何 DNS 查询)。

要允许服务器证书验证,必须将一个或者更多个根证书放置在用户主目录下的~/.postgresql/root.crt文件中(在Microsoft Windows上该文件名为%APPDATA%\postgresql\root.crt)。如果需要把服务器发来的证书链链接到存储在客户端的根证书,还应该将中间证书加到该文件中。

如果文件~/.postgresql/root.crl存在(微软 Windows 上的%APPDATA%\postgresql\root.crl),证书撤销列表(CRL)项也会被检查。

可以通过设置连接参数 sslrootcertsslcrl,或环境变量 PGSSLROOTCERTPGSSLCRL,更改根证书文件与 CRL 的位置。

Note

为了与 PostgreSQL 的早期版本达到向后兼容,如果存在一个根 CA 文件,sslmode=require的行为将与verify-ca相同,即服务器证书根据 CA 验证。我们鼓励依赖这种行为,并且需要证书验证的应用应该总是使用verify-ca或者verify-full

33.18.2. 客户端证书 #

如果服务器尝试通过请求客户端的叶证书来验证客户端的身份, libpq将发送存储在文件 ~/.postgresql/postgresql.crt中的证书,该文件位于用户的主目录中。 证书必须链到服务器信任的根证书。匹配的 私钥文件~/.postgresql/postgresql.key也必须存在。 在Microsoft Windows上,这些文件的名称分别为 %APPDATA%\postgresql\postgresql.crt%APPDATA%\postgresql\postgresql.key。 证书和密钥文件的位置可以通过连接参数 sslcertsslkey, 或通过环境变量PGSSLCERTPGSSLKEY来覆盖。

在Unix系统上,私钥文件的权限必须禁止任何对世界或组的访问;可以通过类似以下命令来实现: chmod 0600 ~/.postgresql/postgresql.key。 或者,该文件可以由root拥有并具有组读取权限(即0640权限)。 该设置适用于由操作系统管理证书和密钥文件的安装。然后,libpq的用户应该成为具有对这些证书和密钥文件访问权限的组的成员。 (在Microsoft Windows上,没有文件权限检查,因为假定%APPDATA%\postgresql目录是安全的。)

postgresql.crt 中的第一个证书必须是客户端证书,因为它必须与客户端私钥匹配。可以选择在文件后面追加中间证书,这样就无需在服务器的 root.crt 文件中存储中间证书。

创建证书的指令请参考Section 18.9.3

33.18.3. 不同模式中提供的保护 #

sslmode参数选择不同的值可以提供不同程度的保护。SSL 可以防范三类攻击:

窃听

如果一个第三方能够检查客户端和服务器之间的网络流量,它能读取连接信息(包括用户名和密码)以及被传递的数据。SSL使用加密来阻止这种攻击。

中间人(MITM

如果一个第三方能对客户端和服务器之间传送的数据进行修改,它就能假装是服务器并且因此能看见并且修改数据,即使这些数据已被加密。然后第三方可以将连接信息和数据转送给原来的服务器,使得它不可能检测到攻击。这样做的通常途径包括 DNS 污染和地址劫持,借此客户端被重定向到一个不同的服务器。还有几种其他的攻击方式能够完成这种攻击。SSL使用证书验证让客户端认证服务器,就可以阻止这种攻击。

模仿

如果一个第三方能假装是一个授权的客户端,它能够简单地访问它本不能访问的数据。通常这可以由不安全的密码管理所致。SSL使用客户端证书来确保只有持有合法证书的客户端才能访问服务器,这样就能阻止这种攻击。

对于一个已知受 SSL 保护的连接,在连接建立之前,必须在客户端和服务器两端都配置 SSL。如果只在服务器端配置,客户端在得知服务器要求高安全性之前,可能就已经开始发送敏感信息(例如密码)。在 libpq 中,要确保连接安全,可以把sslmode参数设置为verify-fullverify-ca,并为系统提供一个用于验证的根证书。这类似于使用https URL浏览加密网页。

一旦服务器已经被认证,客户端可以传递敏感数据。这意味着直到这一点,客户端都不需要知道是否证书将被用于认证,这样只需要在服务器配置中指定就比较安全。

所有SSL选项都带来了加密和密钥交换的负荷,因此必须在性能和安全性之间做出平衡。Table 33.1不同sslmode值所保护的风险,以及它们是怎样看待安全性和负荷的。

Table 33.1. SSL 模式描述

sslmode 窃听保护 MITM 防护 声明
disable 我不关心安全性,并且我不想为加密增加负荷。
allow 可能 我不关心安全性,但如果服务器坚持,我将承担加密带来的负荷。
prefer 可能 我不关心安全性,但如果服务器支持,我希望承担加密带来的负荷。
require 我想要对数据加密,并且我接受因此带来的负荷。我信任该网络会保证我总是连接到想要连接的服务器。
verify-ca 取决于 CA 策略 我想要对数据加密,并且我接受因此带来的负荷。我想要确保我连接到的是我信任的服务器。
verify-full 我想要对数据加密,并且我接受因此带来的负荷。我想要确保我连接到的是我信任的服务器,并且就是我指定的那一个。

verify-caverify-full之间的区别取决于根CA的策略。如果使用了一个公共CAverify-ca允许连接到那些可能已经被其他人注册到该CA的服务器。在这种情况下,总是应该使用verify-full。如果使用了一个本地CA或者甚至是一个自签名的证书,使用verify-ca常常就可以提供足够的保护。

sslmode的默认值是prefer。如表中所示,这在安全性的角度来说没有意义,并且它只承诺可能的性能负荷。提供它作为默认值只是为了向后兼容,并且我们不推荐在安全部署中使用它。

33.18.4. SSL 客户端文件使用 #

Table 33.2总结了与客户端 SSL 设置相关的文件。

Table 33.2. libpq/客户端 SSL 文件用法

文件 内容 效果
~/.postgresql/postgresql.crt 客户端证书 发送到服务器
~/.postgresql/postgresql.key 客户端私钥 证明客户端证书是由拥有者发送;不代表证书拥有者可信
~/.postgresql/root.crt 可信的证书机构 检查服务器证书是由一个可信的证书机构签发
~/.postgresql/root.crl 被证书机构撤销的证书 服务器证书不能在这个列表上

33.18.5. SSL 库初始化 #

如果您的应用程序初始化libssl和/或libcrypto库,并且libpq 构建时带有SSL支持,您应该调用PQinitOpenSSL告诉libpq libssl和/或libcrypto库已被您的应用程序初始化,以便 libpq不会再初始化这些库。

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要么都不初始化的应用足够用了。

PQinitSSLPostgreSQL 8.0 就存在了, 而PQinitOpenSSL直到PostgreSQL 8.4 才被加入,因此PQinitSSL可能对那些需要与旧版本libpq一起工作的应用来说更合适。