选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0
历史版本PostgreSQL 9.3 已于 2018 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

31.18. SSL 支持 #

PostgreSQL 原生支持使用 SSL 连接来加密客户端与服务器之间的通信,以提高安全性。有关服务器端 SSL 功能的详细信息,请参见 第 17.9 节

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

31.18.1. 服务器证书的客户端验证 #

默认情况下,PostgreSQL 不会对服务器证书执行任何验证。这意味着可以在客户端不知情的情况下伪造服务器身份,例如修改 DNS 记录或接管服务器的 IP 地址。要防止身份伪造,客户端必须能够通过信任链验证服务器身份。建立信任链的方法是:在一台计算机上放置根证书机构(CA)的自签名证书,在另一台计算机上放置由根证书签发的叶证书。也可以使用由根证书签发、又用于签发叶证书的中间证书。

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

建立信任链后,客户端可以通过两种方式验证服务器发送的叶证书。如果参数 sslmode 设为 verify-ca,libpq 会沿证书链检查到存储在客户端上的根证书,以验证服务器是否可信。如果 sslmode 设为 verify-full,libpq 还会验证服务器主机名是否与服务器证书中存储的名称匹配。如果无法验证服务器证书,SSL 连接将失败。在大多数对安全敏感的环境中,建议使用 verify-full

verify-full 模式下,会将主机名与证书的 cn(通用名称)属性匹配。如果 cn 属性以星号(*)开头,该星号会被视为通配符,匹配点(.)以外的所有字符。这意味着该证书不会匹配子域。如果使用 IP 地址而不是主机名建立连接,则会匹配该 IP 地址(不执行任何 DNS 查询)。

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

如果文件~/.postgresql/root.crl存在(微软 Windows 上的%APPDATA%\postgresql\root.crl),证书撤销列表(CRL)项也会被检查。

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

注意

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

31.18.2. 客户端证书 #

如果服务器尝试通过请求客户端的叶证书来验证客户端的身份, 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 中的第一个证书必须是客户端证书,因为它必须与客户端私钥匹配。可以选择在文件后面追加中间证书 — 这样就无需在服务器的 root.crt 文件中存储中间证书。

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

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

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

窃听

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

中间人(MITM

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

模仿

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

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

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

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

表 31.1. SSL 模式描述

sslmode 窃听保护 MITM 防护 声明
disable 我不关心安全性,并且我不想为加密增加负荷。
allow 可能 我不关心安全性,但如果服务器坚持,我将承担加密带来的负荷。
prefer 可能 我不关心安全性,但如果服务器支持,我希望承担加密带来的负荷。
require 我想要对数据加密,并且我接受因此带来的负荷。我信任该网络会保证我总是连接到想要连接的服务器。
verify-ca 取决于 CA 策略 我想要对数据加密,并且我接受因此带来的负荷。我想要确保我连接到的是我信任的服务器。
verify-full 我想要对数据加密,并且我接受因此带来的负荷。我想要确保我连接到的是我信任的服务器,并且就是我指定的那一个。

verify-caverify-full之间的区别取决于根CA的策略。如果使用了一个公共CAverify-ca允许连接到那些可能已经被其他人注册到该CA的服务器。在这种情况下,总是应该使用verify-full。如果使用了一个本地CA或者甚至是一个自签名的证书,使用verify-ca常常就可以提供足够的保护。

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

31.18.4. SSL 客户端文件使用 #

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

表 31.2. libpq/客户端 SSL 文件用法

文件 内容 效果
~/.postgresql/postgresql.crt 客户端证书 发送到服务器
~/.postgresql/postgresql.key 客户端私钥 证明客户端证书是由拥有者发送;不代表证书拥有者可信
~/.postgresql/root.crt 可信的证书机构 检查服务器证书是由一个可信的证书机构签发
~/.postgresql/root.crl 被证书机构撤销的证书 服务器证书不能在这个列表上

31.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要么都不初始化的应用足够用了。

PQinitSSLPostgreSQL 8.0 就存在了, 而PQinitOpenSSL直到PostgreSQL 8.4 才被加入,因此PQinitSSL可能对那些需要与旧版本libpq一起工作的应用来说更合适。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。