31.18. SSL 支持

PostgreSQL本地支持使用SSL连接加密客户端/服务器通信以提高安全性。关于服务器端的SSL功能详见Section 17.9

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

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

默认情况下,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中(在微软 Windows 上该文件被命名为%APPDATA%\postgresql\root.crt)。

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

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

Note: 为了与 PostgreSQL 的早期版本达到向后兼容,如果存在一个根 CA 文件,sslmode=require的行为将与verify-ca相同,即服务器证书根据 CA 验证。我们鼓励依赖这种行为,并且需要证书验证的应用应该总是使用verify-ca或者verify-full

31.18.2. 客户端证书

如果服务器要求一个可信的客户端证书,libpq将发送用户主目录中~/.postgresql/postgresql.crt文件存储的证书。该证书必须由一个受服务器信任的证书机构(CA)签发。也必须存在一个私钥文件~/.postgresql/postgresql.key。该私钥文件不能允许全部用户或者组用户的任何访问,可以通过命令chmod 0600 ~/.postgresql/postgresql.key实现。在微软 Windows 上这些文件被命名为%APPDATA%\postgresql\postgresql.crt%APPDATA%\postgresql\postgresql.key,不会有特别的权限检查,因为该目录已经被假定为安全。证书和密钥文件的位置可以使用连接参数sslcertsslkey或者环境变量PGSSLCERTPGSSLKEY覆盖。

在某些情况中,客户端证书可能由一个"中间"证书机构而不是一个服务器直接信任的机构签发。要使用这样一个证书,将签发机构的证书加入到postgresql.crt文件,然后是它的上级机构的证书,并且一直到一个受服务器信任的"根"机构。在每一种postgresql.crt包含多于一个证书的情况中,根证书都应该被包括。

注意root.crt列出了被认为可信的能用于签发服务器证书的顶层 CA。原则上不需要列出签发客户端证书的 CA,大部分情况下这些 CA 也被信任可以用于服务器证书。

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

sslmode参数的不同值提供了不同级别的保护。SSL 能够针对三类攻击提供保护:

窃听

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

中间人(MITM

如果一个第三方能对客户端和服务器之间传送的数据进行修改,它就能假装是服务器并且因此能看见并且修改数据,即使这些数据已被加密。然后第三方可以将连接信息和数据转送给原来的服务器,使得它不可能检测到攻击。这样做的通常途径包括 DNS 污染和地址劫持,借此客户端被重定向到一个不同的服务器。还有几种其他的攻击方式能够完成这种攻击。SSL使用证书验证让客户端认证服务器,就可以阻止这种攻击。

模仿

如果一个第三方能假装是一个授权的客户端,它能够简单地访问它本不能访问的数据。通常这可以由不安全的口令管理所致。SSL使用客户端证书来确保只有持有合法证书的客户端才能访问服务器,这样就能阻止这种攻击。

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

一旦服务器已经被认证,客户端可以传递敏感数据。这意味着直到这一点,客户端都不需要知道是否证书将被用于认证,这样只需要在服务器配置中指定就比较安全。

所有SSL选项都带来了加密和密钥交换的负荷,因此必须在性能和安全性之间做出平衡。Table 31-1不同sslmode值所保护的风险,以及它们是怎样看待安全性和负荷的。

Table 31-1. SSL 模式描述

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

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

sslmode的默认值是prefer。如表中所示,这在安全性的角度来说没有意义,并且它只承诺可能的性能负荷。提供它作为默认值只是为了向后兼容,并且我们不推荐在安全部署中使用它。

31.18.4. SSL 客户端文件使用

Table 31-2总结了与客户端 SSL 设置相关的文件。

Table 31-2. Libpq/客户端 SSL 文件用法

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

31.18.5. SSL 库初始化

如果你的应用初始化libssllibcrypto库以及带有SSL支持的libpq,你应该调用PQinitOpenSSL来告诉libpqlibssllibcrypto库已经被你的应用初始化,这样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要么都不初始化的应用足够用了。

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