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
模式下,主机名将与证书的主题备用名称属性 (SAN) 匹配,或者如果不存在类型为 dNSName
的 SAN,则与通用名称属性匹配。如果证书的名称属性以星号 (*
) 开头,则星号将被视为通配符,它将匹配除了点 (.
) 之外的所有字符。这意味着证书将不匹配子域。如果使用 IP 地址而不是主机名进行连接,则将 IP 地址与类型为 iPAddress
或 dNSName
的 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) 条目。
可以通过设置连接参数 sslrootcert
和 sslcrl
或环境变量 PGSSLROOTCERT
和 PGSSLCRL
来更改根证书文件和 CRL 的位置。 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 系统上,私钥文件的权限必须不允许世界或组访问;可以通过 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
URL进行加密的网页浏览。
一旦服务器被身份验证,客户端就可以传递敏感数据。这意味着在此之前,客户端不需要知道是否会使用证书进行身份验证,因此可以在服务器配置中安全地指定它。
所有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 中添加的,因此 PQinitSSL
可能更适合需要与旧版本的 libpq 一起使用的应用程序。
如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符,或者需要进一步说明,请使用此表单报告文档问题。