Postgresql 中文操作指南
34.19. SSL Support #
PostgreSQL 本机支持使用 SSL 连接,使用 TLS 协议加密客户端/服务器通信,以提高安全性。有关服务器端 SSL 功能的详细信息,请参见 Section 19.9。
libpq 读取系统范围的 OpenSSL 配置文件。默认情况下,此文件名为 openssl.cnf,位于 openssl version -d 报告的目录中。可以将环境变量 OPENSSL_CONF 设置为所需配置文件的名称,从而覆盖此默认值。
34.19.1. Client Verification of Server Certificates #
默认情况下,PostgreSQL 不会对服务器证书执行任何验证。这意味着可以在客户端不知情的情况下欺骗服务器身份(例如,通过修改 DNS 记录或接管服务器 IP 地址)。为了防止欺骗,客户端必须能够通过信任链验证服务器身份。通过在一台计算机上放置根(自签名)证书颁发机构 (CA) 证书和由根证书在另一台计算机上签署的叶证书 signed,来建立信任链。也可使用由根证书签署并签署叶证书的“中间”证书。
若要允许客户端验证服务器身份,请在客户端上放置一个根证书,并在服务器上放置一个由根证书签署的叶证书。为了允许服务器验证客户端的身份,请在服务器上放置一个根证书,并在客户端上放置一个由根证书签署的叶证书。一个或多个中间证书(通常与叶证书存储在一起)也可以用于将叶证书链接到根证书。
一旦建立了信任链,客户端就可以通过两种方式验证服务器发送的叶证书。如果将参数 sslmode 设置为 verify-ca,libpq 将通过检查客户端上存储的根证书的证书链来验证服务器可信。如果 sslmode 设置为 verify-full,libpq 将 also 验证服务器主机名是否与存储在服务器证书中的名称匹配。如果无法验证服务器证书,SSL 连接将会失败。在大多数安全敏感的环境中,建议采用 verify-full。
在 verify-full 模式下,将主机名与证书的主题备用名称属性(SAN)或通用名称属性进行匹配(如果不存在类型为 dNSName 的 SAN 时)。如果证书的名称属性以星号 (*) 开头,星号将被视为通配符,它将匹配包含 except 点 (.) 的所有字符。这意味着证书将不匹配子域。如果使用 IP 地址而不是主机名建立连接,则该 IP 地址将与类型为 iPAddress 或 dNSName 的 SAN(不执行任何 DNS 查询)进行匹配。如果没有 iPAddress SAN 且不存在匹配的 dNSName SAN,则主机 IP 地址将与通用名称属性进行匹配。
Note
为了与 PostgreSQL 的早期版本向后兼容,将以不同于 RFC 6125的方式验证主机 IP 地址。主机 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 文件的目录。
Note
为了向后兼容 PostgreSQL 的早期版本,如果存在根 CA 文件,sslmode=require 的行为将与 verify-ca 的行为相同,这意味着服务器证书将针对 CA 验证。不建议依赖此行为,需要证书验证的应用程序应始终使用 verify-ca 或 verify-full。
34.19.2. Client Certificates #
如果服务器通过请求客户端的叶证书来尝试验证客户端身份,则 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 选项不存在或为空,则 OpenSSL 会在有 TTY 时使用 Enter PEM pass phrase: 提示交互式地提示输入密码。应用程序可以通过提供自己的密钥密码回调来覆盖客户端证书提示和 sslpassword 参数的处理。请参阅 PQsetSSLKeyPassHook_OpenSSL 。
有关创建证书的说明,请参见 Section 19.9.5。
34.19.3. Protection Provided in Different Modes #
sslmode 参数的不同值提供了不同的保护级别。SSL 可以针对三种类型的攻击提供保护:
-
Eavesdropping
-
如果第三方可以检查客户端和服务器之间的网络流量,则它可以读取连接信息(包括用户名和密码)以及所传递的数据。SSL 使用加密来防止这种情况。
-
-
Man-in-the-middle (MITM)
-
如果第三方可以在客户端和服务器之间传递时修改数据,则它可以伪装成服务器,从而看到和修改数据_even if it is encrypted_。随后,第三方可以将连接信息和数据转发给原始服务器,从而无法检测到此攻击。执行此操作的常用媒介包括 DNS 欺骗和地址劫持,在此过程中,客户端被引导至与预期不同的服务器。还有其他几种可以实现此目的的攻击方法。SSL 使用证书验证来防止这种情况,方法是向客户端验证服务器。
-
-
Impersonation
-
如果第三方可以伪装成经授权的客户端,则它可以简单地访问本无权访问的数据。这通常可以通过不安全的密码管理发生。SSL 使用客户端证书来防止这种情况,确保只有持有有效证书者才能访问服务器。
-
为了让连接成为众所周知的安全 SSL 连接,必须在建立连接之前在_both the client and the server_上配置 SSL 使用情况。如果仅在服务器上配置,客户端可能在得知服务器需要高安全性的情况下即发送敏感信息(例如密码)。在 libpq 中,可以通过将_sslmode_ 参数设置为_verify-full_ 或 verify-ca 来确保安全连接,并为系统提供一个根证书以供验证。这类似于将 https URL 用于加密 Web 浏览。
经过服务器验证后,客户端可以传递敏感数据。这意味着在此之前,客户端无需知道是否会使用证书进行身份验证,因此可以安全地在服务器配置中指定仅使用证书。
所有 SSL 选项都以加密和密钥交换的形式带来开销,因此必须在性能和安全性之间进行权衡。 Table 34.1说明了不同的 _sslmode_值可以防范哪些风险,以及它们对安全性和开销的说明。
Table 34.1. SSL Mode Descriptions
sslmode |
Eavesdropping protection |
MITM protection |
Statement |
disable |
No |
No |
我不关心安全性且不想支付加密开销。 |
allow |
Maybe |
No |
我并不关心安全性,如果服务器要求加密,我会支付加密开销。 |
prefer |
Maybe |
No |
我并不关心加密,但如果服务器支持加密,我希望支付加密开销。 |
require |
Yes |
No |
我希望我的数据被加密,我接受加密开销。我信任网络连接总是连接到我想要的服务器。 |
verify-ca |
Yes |
Depends on CA policy |
我希望我的数据被加密,我接受加密开销。我想确保连接到一个我信任的服务器。 |
verify-full |
Yes |
Yes |
我希望我的数据被加密,我接受加密开销。我希望确保我连接到一个我信任的服务器,并且那是我指定的服务器。 |
verify-ca 和 verify-full 之间的差异取决于根 CA 的策略。如果使用公有 CA,verify-ca 允许连接到 somebody else 可能已向 CA 注册的服务器。在这种情况下,应始终使用 verify-full。如果使用本地 CA,甚至自签名证书,使用 verify-ca 通常可提供足够的保护。
sslmode 的默认值为 prefer。如表中所示,从安全的角度来看,这毫无意义,如果可能,它只会造成性能开销。它仅作为向后兼容性的默认值提供,并且在安全部署中不建议使用。
34.19.4. SSL Client File Usage #
Table 34.2总结了与客户端 SSL 设置相关的文件。
Table 34.2. Libpq/Client SSL File Usage
File |
Contents |
Effect |
~/.postgresql/postgresql.crt |
client certificate |
sent to server |
~/.postgresql/postgresql.key |
client private key |
证明了所有者发送的客户证书;没有表明证书所有者是否值得信赖 |
~/.postgresql/root.crt |
trusted certificate authorities |
检查服务器证书是否是由受信任的证书颁发机构签名的 |
~/.postgresql/root.crl |
证书颁发机构吊销的证书 |
服务器证书不得出现在此列表中 |
34.19.5. SSL Library Initialization #
如果您的应用程序初始化了 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 库,则在首次打开数据库连接之前,您_must_ 使用合适的参数以零值调用此函数。还要确保在打开数据库连接之前完成该初始化。
-
PQinitSSL #
-
-
允许应用程序选择要初始化哪些安全库。
void PQinitSSL(int do_ssl);
-
此函数等效于 PQinitOpenSSL(do_ssl, do_ssl)。对于同时或都不初始化 OpenSSL 和 libcrypto 的应用程序来说,此函数已足够。
-
PQinitSSL 自PostgreSQL 8.0起就已存在,而 PQinitOpenSSL 在PostgreSQL 8.4中添加,因此 PQinitSSL 对于需要使用较旧版本的libpq的应用程序来说可能更好。