↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 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
历史版本。 PostgreSQL 12 已结束支持。 2024-11-21. 请参阅 当前版本手册.

33.18. SSL 支持 #

PostgreSQL 原生支持使用 SSL 连接来加密客户端与服务器之间的通信,以提高安全性。有关服务器端 SSL 功能的详细信息,请参见第 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)项也会被检查。

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

注意

为与 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。可以通过连接参数 sslcert 和 sslkey,或环境变量 PGSSLCERT 和 PGSSLKEY,覆盖证书和密钥文件的位置。

在 Unix 系统上,私钥文件的权限必须禁止所属组及其他用户的任何访问;可以用 chmod 0600 ~/.postgresql/postgresql.key 这样的命令实现。另一种做法是使文件归 root 所有,并允许组用户读取(即 0640 权限)。这种设置适用于由操作系统管理证书和密钥文件的安装环境。此时,应将 libpq 用户加入有权访问这些证书和密钥文件的组。(在 Microsoft Windows 上,不检查文件权限,因为假定 %APPDATA%\postgresql 目录是安全的。)

postgresql.crt 中的第一个证书必须是客户端的证书,因为它必须匹配客户端的私钥。可以选择将“中间”证书追加到该文件 — 这样做避免了在服务器上存放中间证书的要求(ssl_ca_file)。

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

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

sslmode 参数的不同值提供不同级别的保护。SSL 可以防范三类攻击:

窃听

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

中间人(MITM)

如果第三方能修改客户端与服务器之间传输的数据,就可以冒充服务器,进而查看和修改数据,即使数据已经加密。随后,第三方可以将连接信息和数据转发给原来的服务器,使攻击无法被察觉。常见的手段包括 DNS 污染和地址劫持,从而将客户端引向预期之外的服务器。还有其他几种攻击手段可以达到同样的目的。SSL 使用证书验证,让客户端认证服务器身份,以防范这种攻击。

冒充

如果第三方能冒充获授权的客户端,就能直接访问其无权访问的数据。这通常可能由不安全的密码管理导致。SSL 使用客户端证书,确保只有持有有效证书的客户端才能访问服务器,以防范这种攻击。

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

服务器通过身份认证后,客户端便可以传送敏感数据。这意味着,在此之前,客户端无需知道是否会使用证书进行认证,因此可以安全地仅在服务器配置中指定这一点。

所有 SSL 选项都会产生加密和密钥交换的开销,因此必须在性能与安全性之间作出权衡。表 33.1 说明了不同 sslmode 值所能防范的风险,以及它们所表达的对安全性和开销的取舍。

表 33.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。如表所示,从安全角度看,这一设置没有意义;它只会在可能时带来性能开销。将其作为默认值仅出于向后兼容的考虑,不建议在有安全要求的部署中使用。

33.18.4. SSL 客户端文件使用 #

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

表 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 不会再初始化这些库。但是,当使用 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 一起工作的应用来说更合适。

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.