2025年9月25日: PostgreSQL 18 发布!
支持的版本:当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本:开发版
不支持的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0

32.19. SSL 支持 #

PostgreSQL 原生支持使用SSL连接来加密客户端/服务器通信,使用TLS协议以增强安全性。有关服务器端功能的详细信息,请参阅第 18.9 节SSL功能。

libpq 读取系统范围的 OpenSSL 配置文件。默认情况下,此文件名为 openssl.cnf,位于 openssl version -d 报告的目录中。可以通过将环境变量 OPENSSL_CONF 设置为所需配置文件的名称来覆盖此默认值。

32.19.1. 客户端验证服务器证书 #

默认情况下,PostgreSQL 不会执行任何服务器证书验证。这意味着,如果不让客户端知道,就可以伪造服务器身份(例如,通过修改 DNS 记录或接管服务器 IP 地址)。为了防止伪造,客户端必须能够通过信任链来验证服务器的身份。信任链的建立方式是将一个根(自签名)证书颁发机构(CA) 证书放在一台计算机上,并将一个由根证书签名的叶子证书放在另一台计算机上。也可以使用一个由根证书签名并签名叶子证书的“中间”证书。

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

一旦建立了信任链,客户端就有两种方法来验证服务器发送的叶子证书。如果参数 sslmode 设置为 verify-ca,libpq 将通过检查证书链到客户端存储的根证书来验证服务器是否可信。如果 sslmode 设置为 verify-full,libpq 将验证服务器主机名是否与服务器证书中存储的名称匹配。如果服务器证书无法验证,SSL 连接将失败。verify-full 在大多数安全敏感环境中推荐使用。

verify-full 模式下,主机名将与证书的主题备用名称(SAN)属性进行匹配,如果不存在 dNSName 类型的 SAN,则与通用名称(Common Name)属性进行匹配。如果证书的名称属性以星号(*)开头,则星号将被视为通配符,它将匹配除点(.)之外的所有字符。这意味着证书将不匹配子域。如果使用 IP 地址而不是主机名进行连接,IP 地址将与 iPAddressdNSName 类型的 SAN 进行匹配(不执行任何 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),也将检查证书吊销列表(CRL)条目。

可以通过设置连接参数 sslrootcertsslcrl 或环境变量 PGSSLROOTCERTPGSSLCRL 来更改根证书文件和 CRL 的位置。还可以使用 sslcrldir 或环境变量 PGSSLCRLDIR 来指定包含 CRL 文件的目录。

注意

为了向后兼容 PostgreSQL 的早期版本,如果存在根 CA 文件,sslmode=require 的行为将与 verify-ca 相同,这意味着服务器证书将根据 CA 进行验证。不鼓励依赖此行为,需要证书验证的应用程序应始终使用 verify-caverify-full

32.19.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 中的第一个证书必须是客户端证书,因为它必须与客户端的私钥匹配。可以选择将“中间”证书附加到文件中——这样做可以避免在服务器上存储中间证书(ssl_ca_file)。

证书和密钥可以是 PEM 或 ASN.1 DER 格式。

密钥可以以明文形式存储,或者使用 OpenSSL 支持的任何算法(如 AES-128)用密码短语加密。如果密钥是加密存储的,则可以在 sslpassword 连接选项中提供密码短语。如果提供了加密密钥并且 sslpassword 选项缺失或为空,如果存在 TTY,OpenSSL 将通过 Enter PEM pass phrase: 提示交互式地提示输入密码。应用程序可以通过提供自己的密钥密码回调来覆盖客户端证书提示和 sslpassword 参数的处理;请参阅 PQsetSSLKeyPassHook_OpenSSL

有关创建证书的说明,请参阅第 18.9.5 节

32.19.3. 不同模式下提供的保护 #

参数 sslmode 的不同值提供不同级别的保护。SSL 可以提供针对三种类型攻击的保护:

窃听

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

中间人(MITM)

如果第三方能够修改数据在客户端和服务器之间传递,它可以假装是服务器,因此即使数据已加密也能看到和修改数据。然后,第三方可以将连接信息和数据转发给原始服务器,使得无法检测到此攻击。常见的攻击方式包括 DNS 投毒和地址劫持,即客户端被导向了非预期的服务器。还有其他几种攻击方法可以实现此目的。SSL通过向客户端进行身份验证来防止这种情况,通过证书验证。

冒充

如果第三方可以冒充合法客户端,它就可以简单地访问本不应访问的数据。通常这可以通过不安全的密码管理来实现。SSL通过客户端证书来防止这种情况,确保只有持有有效证书的人才能访问服务器。

要使连接被认为是 SSL 安全的,必须在连接建立之前在客户端和服务器上都配置 SSL 使用。如果仅在服务器上配置,客户端可能会在知道服务器需要高安全性之前就发送敏感信息(例如,密码)。在 libpq 中,可以通过将 sslmode 参数设置为 verify-fullverify-ca,并为系统提供一个用于验证的根证书来确保安全连接。这类似于使用 httpsURL进行加密的网络浏览。

一旦服务器通过身份验证,客户端就可以传递敏感数据。这意味着直到此时,客户端都不需要知道证书是否将用于身份验证,因此可以在服务器配置中仅指定这一点是安全的。

全部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.19.4. SSL 客户端文件使用 #

表 32.2 总结了与客户端 SSL 设置相关的文件的信息。

表 32.2. Libpq/Client SSL 文件使用

文件 目录 效果
~/.postgresql/postgresql.crt 客户端证书 发送到服务器
~/.postgresql/postgresql.key 客户端私钥 证明客户端证书由所有者发送;不表示证书所有者值得信任
~/.postgresql/root.crt 受信任的证书颁发机构 检查服务器证书是否由受信任的证书颁发机构签名
~/.postgresql/root.crl 证书颁发机构吊销的证书 服务器证书不得在此列表中

32.19.5. SSL 库初始化 #

需要与旧版 PostgreSQL(使用 OpenSSL 1.0.2 或更早版本)兼容的应用程序在使用 SSL 库之前需要初始化该库。初始化 libssl 和/或 libcrypto 库的应用程序应调用 PQinitOpenSSL 来告知 libpq libssl 和/或 libcrypto 库已被您的应用程序初始化,这样 libpq 就不会再次初始化这些库。但是,在使用 OpenSSL 1.1.0 或更高版本时,不需要这样做,因为重复初始化不再有问题。

有关其使用方法的详细信息,请参阅您正在针对的 PostgreSQL 版本的文档。

PQinitOpenSSL #

允许应用程序选择要初始化的安全库。

void PQinitOpenSSL(int do_ssl, int do_crypto);

此函数已弃用,仅为向后兼容而存在,它不执行任何操作。

PQinitSSL #

允许应用程序选择要初始化的安全库。

void PQinitSSL(int do_ssl);

此函数等同于 PQinitOpenSSL(do_ssl, do_ssl)。此函数已弃用,仅为向后兼容而存在,它不执行任何操作。

PQinitSSLPQinitOpenSSL 被保留用于向后兼容,但在 PostgreSQL 18 后不再需要。PQinitSSLPostgreSQL 8.0 起存在,而 PQinitOpenSSLPostgreSQL 8.4 中添加,因此对于需要与旧版 libpq 兼容的应用程序,PQinitSSL 可能更可取。

提交更正

如果您在文档中看到任何不正确、与您对特定功能的使用体验不符或需要进一步澄清的内容,请使用此表单报告文档问题。