pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PostgreSQL 原生支持使用 SSL 连接来加密客户端与服务器之间的通信,以提高安全性。有关服务器端 SSL 功能的详细信息,请参见 第 17.8 节。
libpq读取系统范围的OpenSSL配置文件。默认情况下,这个文件被命名为openssl.cnf并且位于openssl version -d所报告的目录中。可以通过设置环境变量OPENSSL_CONF把这个默认值覆盖为想要的配置文件的名称。
默认情况下,PostgreSQL不会对服务器证书执行任何验证。这意味着可以在客户端不知情的情况下伪造服务器身份,例如修改 DNS 记录或接管服务器的 IP 地址。为了防止伪造,必须使用SSL证书验证。
如果参数sslmode设为verify-ca,libpq 会沿证书链检查到一个受信任的证书机构(CA),以验证服务器是否可信。如果sslmode设为verify-full,libpq 还会验证服务器主机名是否与其证书匹配。如果无法验证服务器证书,SSL 连接将失败。在大多数对安全敏感的环境中,建议使用verify-full。
在 verify-full 模式下,会将主机名与证书的 cn(通用名称)属性匹配。如果 cn 属性以星号(*)开头,该星号会被视为通配符,匹配除点(.)以外的所有字符。这意味着该证书不会匹配子域。如果使用 IP 地址而不是主机名建立连接,则会匹配该 IP 地址(不执行任何 DNS 查询)。
要允许服务器证书验证,必须将一个或多个受信任的CA的证书放置在用户主目录下的~/.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。
如果服务器请求一个受信任的客户端证书,libpq将发送存储在用户主目录下的文件~/.postgresql/postgresql.crt中的证书。该证书必须由服务器信任的证书机构(CA)之一签发。匹配的私钥文件~/.postgresql/postgresql.key也必须存在。私钥文件的权限必须不允许任何对世界或组的访问;可以通过命令chmod 0600 ~/.postgresql/postgresql.key实现。在 Microsoft Windows 上,这些文件的名称分别为%APPDATA%\postgresql\postgresql.crt和%APPDATA%\postgresql\postgresql.key,并且不进行特别的权限检查,因为该目录被假定为安全的。证书和密钥文件的位置可以通过连接参数sslcert和sslkey或环境变量PGSSLCERT和PGSSLKEY来覆盖。
在某些情况下,客户端证书可能由一个“中间”证书机构签发,而不是由服务器直接信任的机构签发。要使用这样的证书,把签发机构的证书追加到postgresql.crt文件中,然后是其父机构的证书,依此类推直到一个服务器信任的“根”机构。在postgresql.crt包含多个证书的所有情况中,都应包含根证书。
注意,root.crt列出的是被认为可以信任其为服务器证书签名的顶层 CA。原则上它不必列出为客户端证书签名的 CA,尽管在大多数情况下该 CA 也会被信任用于服务器证书。
sslmode参数的不同值提供不同级别的保护。SSL 可以防范三类攻击:
表 31.2. SSL 攻击
| Type | Description |
|---|---|
| 窃听 | 如果一个第三方能够检查客户端和服务器之间的网络流量,它能读取连接信息(包括用户名和密码)以及被传递的数据。SSL使用加密来阻止这种攻击。 |
| 中间人攻击(MITM) | 如果第三方能修改客户端与服务器之间传输的数据,就可以冒充服务器,进而查看和修改数据,即使数据已经加密。随后,第三方可以将连接信息和数据转发给原来的服务器,使攻击无法被察觉。常见的手段包括 DNS 污染和地址劫持,从而将客户端引向预期之外的服务器。还有其他几种攻击手段可以达到同样的目的。SSL使用证书验证,让客户端认证服务器身份,以防范这种攻击。 |
| 冒充 | 如果第三方能冒充获授权的客户端,就能直接访问其无权访问的数据。这通常可能由不安全的密码管理导致。SSL使用客户端证书,确保只有持有有效证书的客户端才能访问服务器,以防范这种攻击。 |
要确保连接安全,必须在建立连接之前,在客户端和服务器两端配置 SSL。如果仅在服务器上配置,客户端可能在得知服务器要求高安全性之前就已发送敏感信息(例如密码)。在 libpq 中,可以将 sslmode 参数设为 verify-full 或 verify-ca,并向系统提供用于验证的根证书,以确保连接安全。这类似于使用 https URL 进行加密的网页浏览。
服务器通过身份认证后,客户端便可以传送敏感数据。这意味着,在此之前,客户端无需知道是否会使用证书进行认证,因此可以安全地仅在服务器配置中指定这一点。
所有SSL选项都以加密和密钥交换的形式带来开销,因此必须在性能和安全性之间做出权衡。下表展示了不同sslmode值所能防范的风险,以及它们在安全性和开销方面所作的表述:
表 31.3. 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。如表所示,从安全角度看,这一设置没有意义;它只会在可能时带来性能开销。将其作为默认值仅出于向后兼容的考虑,不建议在有安全要求的部署中使用。
表 31.4. libpq/客户端 SSL 文件用法
| 文件 | 内容 | 效果 |
|---|---|---|
~/.postgresql/postgresql.crt |
客户端证书 | 发送到服务器 |
~/.postgresql/postgresql.key |
客户端私钥 | 证明客户端证书是由拥有者发送;不代表证书拥有者可信 |
~/.postgresql/root.crt |
可信的证书机构 | 检查服务器证书是由一个可信的证书机构签发 |
~/.postgresql/root.crl |
被证书机构撤销的证书 | 服务器证书不能在这个列表上 |
如果你的应用初始化了libssl和/或libcrypto库,而libpq构建时带有SSL支持,你应该调用PQinitOpenSSL来告知libpq,libssl和/或libcrypto库已经被你的应用初始化,这样libpq就不会再初始化那些库。 SSL API 的详细信息见 http://h71000.www7.hp.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要么都不初始化的应用足够用了。
PQinitSSL从PostgreSQL 8.0 就存在了, 而PQinitOpenSSL直到PostgreSQL 8.4 才被加入,因此PQinitSSL可能对那些需要与旧版本libpq一起工作的应用来说更合适。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。