34.18. SSL 支持

PostgreSQL 对使用 SSL 连接加密 Client 端/服务器通信以提高安全性提供了本机支持。有关服务器端 SSL 功能的详细信息,请参见Section 18.9

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

34 .18.1. Client 端验证服务器证书

默认情况下,PostgreSQL 将不对服务器证书进行任何验证。这意味着可以在 Client 端不知情的情况下欺骗服务器身份(例如,通过修改 DNS 记录或接管服务器 IP 地址)。为了防止欺骗,Client 端必须能够通过信任链来验证服务器的身份。通过在一台计算机上放置根(自签名)证书颁发机构(CA)证书,并在另一台计算机上放置由根证书“签名”的叶证书来构建信任链。也可以使用由根证书签名和签署叶子证书的“中间”证书。

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

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

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

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

如果文件~/.postgresql/root.crl存在(在 Microsoft Windows 上为%APPDATA%\postgresql\root.crl),还将检查证书吊销列表(CRL)条目。

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

Note

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

34 .18.2. Client 证书

如果服务器尝试通过请求 Client 端的叶子证书来验证 Client 端的身份,则 libpq 将发送存储在用户主目录中文件~/.postgresql/postgresql.crt中的证书。证书必须链接到服务器信任的根证书。匹配的私钥文件~/.postgresql/postgresql.key也必须存在。私钥文件不得允许任何访问世界或组的信息;通过命令chmod 0600 ~/.postgresql/postgresql.key实现。在 Microsoft Windows 上,这些文件名为%APPDATA%\postgresql\postgresql.crt%APPDATA%\postgresql\postgresql.key,并且由于目录被认为是安全的,因此没有特殊的权限检查。证书和密钥文件的位置可以由连接参数sslcertsslkey或环境变量PGSSLCERTPGSSLKEY覆盖。

postgresql.crt中的第一个证书必须是 Client 端的证书,因为它必须与 Client 端的私钥匹配。可以选择将“中间”证书附加到文件中-这样做避免了需要在服务器(ssl_ca_file)上存储中间证书。

有关创建证书的说明,请参见Section 18.9.5

34 .18.3. 提供不同模式的保护

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

  • Eavesdropping

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

    • 如果第三方可以在 Client 端和服务器之间传递数据时修改数据,则它可以 Feign 是服务器,因此即使已加密*,也可以查看和修改数据。然后,第三方可以将连接信息和数据转发到原始服务器,从而无法检测到此攻击。这样做的常见媒介包括 DNS 中毒和地址劫持,从而将 Client 端定向到与预期不同的服务器。还有其他几种攻击方法可以实现此目的。 SSL 通过向 Client 端验证服务器来使用证书验证来防止这种情况。
  • Impersonation

    • 如果第三方可以伪装成授权 Client,则可以简单地访问它不应该访问的数据。通常,这可能通过不安全的密码 Management 发生。 SSL 通过确保只有有效证书的持有者才能访问服务器,从而使用 Client 端证书来防止这种情况。

为了使连接安全,必须在Client 端和服务器上配置 SSL 用法,然后才能构建连接。如果仅在服务器上进行配置,则 Client 端可能在知道服务器需要高度安全性之前最终发送敏感信息(例如密码)。在 libpq 中,可以通过将sslmode参数设置为verify-fullverify-ca并为系统提供根证书进行验证来确保安全连接。这类似于使用https URL 进行加密的 Web 浏览。

服务器通过身份验证后,Client 端即可传递敏感数据。这意味着到目前为止,Client 端无需知道是否将证书用于身份验证,因此可以安全地仅在服务器配置中指定证书。

所有 SSL 选项都以加密和密钥交换的形式带来开销,因此必须在性能和安全性之间进行权衡。 Table 34.1说明了不同的sslmode值可以防范的风险,以及它们关于安全性和开销的 Statements。

表 34.1 SSL 模式说明

sslmodeEavesdropping protectionMITM protectionStatement
disableNoNo我不在乎安全性,也不想支付加密的开销。
allowMaybeNo我不在乎安全性,但是如果服务器坚持要求,我将支付加密的开销。
preferMaybeNo我不在乎加密,但是如果服务器支持,我希望支付加密的开销。
requireYesNo我希望对数据进行加密,并且我接受开销。我相信网络将确保我始终连接到所需的服务器。
verify-caYes取决于 CAPolicy我希望对数据进行加密,并且我接受开销。我想确保我连接到我信任的服务器。
verify-fullYesYes我希望对数据进行加密,并且我接受开销。我想确保我连接到我信任的服务器,并且这是我指定的服务器。

verify-caverify-full之间的差异取决于根 CA 的策略。如果使用公共 CA,则verify-ca允许连接到其他可能已向 CA 注册的服务器。在这种情况下,应始终使用verify-full。如果使用了本地 CA,甚至使用了自签名证书,则使用verify-ca通常可以提供足够的保护。

sslmode的默认值为prefer。如表中所示,从安全角度来看,这毫无意义,并且仅在可能的情况下保证性能开销。仅将它作为向后兼容的默认值提供,在安全部署中不建议使用。

34 .18.4. SSLClient 端文件使用情况

Table 34.2总结了与 Client 端上的 SSL 设置相关的文件。

表 34.2. Libpq /Client 端 SSL 文件使用情况

FileContentsEffect
~/.postgresql/postgresql.crtclient certificate服务器请求
~/.postgresql/postgresql.keyClient 私钥证明所有者发送的 Client 证书;并不表示证书所有者是可信赖的
~/.postgresql/root.crt受信任的证书颁发机构检查服务器证书是否由受信任的证书颁发机构签名
~/.postgresql/root.crl证书颁发机构吊销的证书服务器证书不能在此列表中

34 .18.5. SSL 库初始化

如果您的应用程序初始化了libssl和/或libcrypto库,并且 libpq 是使用 SSL 支持构建的,则应调用PQinitOpenSSL告诉 libpq libssl和/或libcrypto库已由您的应用程序初始化,因此 libpq 也不会初始化这些库。有关 SSL API 的详细信息,请参见http://h41379.www4.hpe.com/doc/83final/ba554_90007/ch04.html

  • 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或都不初始化的应用程序就足够了。

从 PostgreSQL 8.0 开始存在PQinitSSL,而在 PostgreSQL 8.4 中添加了PQinitOpenSSL,因此对于需要使用旧版本 libpq 的应用程序,PQinitSSL可能更可取。