PostgreSQL 具有使用SSL连接的本机支持,使用TLS提高安全性协议。有关于服务器端功能的详细信息,请参见第 18.9 节。SSL。
libpq读取全局OpenSSL配置文件。默认情况下,此文件命名为openssl.cnf
,并位于由openssl
报告的目录中。可以通过将环境变量OPENSSL_CONF
设置为所需的配置文件名称来替代此默认值。
默认情况下,PostgreSQL不会对服务器证书执行任何验证。这意味着可以欺骗服务器身份(例如通过修改 DNS 记录或接管服务器 IP 地址),而客户端却不知情。为了防止欺骗,客户端必须能够通过信任链验证服务器的身份。通过将根(自签名)证书颁发机构 (CA) 证书放置在某个计算机上,而由根证书在另一个计算机上签名的叶证书signed来建立信任链。还可以使用根证书签名的同时签名叶证书的“中间”证书。
为了允许客户端验证服务器的身份,请在客户端上放置一个根证书,并在服务器上放置一个由根证书签名的叶证书。为了允许服务器验证客户端的身份,请在服务器上放置一个根证书,并在客户端上放置一个由根证书签名的叶证书。一个或多个中间证书(通常与叶证书一起存储)也可以用来将叶证书链接到根证书。
建立信任链后,客户端有两种方法可以验证服务器发送的叶证书。如果将参数sslmode
设置为verify-ca
,libpq 将通过检查到客户端上存储的根证书的证书链来验证服务器的值得信赖性。如果sslmode
设置为verify-full
,libpq 将也验证服务器主机名是否与存储在服务器证书中的名称相匹配。如果无法验证服务器证书,SSL 连接将失败。verify-full
在大多数注重安全的环境中都是推荐的。
在 verify-full
模式中,主机名将与证书的主题备用名称属性 (SAN) 匹配,或在无 dNSName
类型的 SAN 时与公用名属性匹配。如果证书的名称属性以星号 (*
) 开头,则将星号视为通配符,它将匹配所有字符 除 点 (.
) 以外的字符。这意味着该证书将不匹配子域名。如果连接使用 IP 地址而不是主机名进行,则该 IP 地址将与 iPAddress
或 dNSName
类型的 SAN 进行匹配(而不进行任何 DNS 查找)。如果无现有的 iPAddress
SAN 且无匹配的 dNSName
SAN,则该主机 IP 地址将与公用名属性进行匹配。
为向后兼容低版本的 PostgreSQL,主机 IP 地址的验证方式与 RFC 6125 不同。主机 IP 地址始终与 dNSName
SAN 和 iPAddress
SAN 匹配,而且如果无相关的 SAN,则可与公用名属性匹配。
要允许服务器证书验证,必须将一个或多个根证书放在用户主目录的 ~/.postgresql/root.crt
文件中。(在 Microsoft Windows 中,此文件名为 %APPDATA%\postgresql\root.crt
。)如果需要将证书链从服务器发送到存储在客户端的根证书上,则还应将中间证书添加到文件中。
如果 ~/.postgresql/root.crl
文件存在(在 Microsoft Windows 中为 %APPDATA%\postgresql\root.crl
),则还将检查证书吊销列表 (CRL) 项。
可以通过设置连接参数 sslrootcert
和 sslcrl
或环境变量 PGSSLROOTCERT
和 PGSSLCRL
来更改根证书文件和 CRL 的位置。还可以使用 sslcrldir
或环境变量 PGSSLCRLDIR
来指定包含 CRL 文件的目录。
为向后兼容低版本的 PostgreSQL,如果存在根 CA 文件,则 sslmode
=require
的行为将与 verify-ca
的行为相同,这意味着服务器证书将根据 CA 验证。不鼓励依赖此行为,需要证书验证的应用程序应始终使用 verify-ca
或 verify-full
。
如果服务器要求客户端的叶子证书以验证客户端标识,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).
证书和密钥可以采用 PEM 或 ASN.1 DER 格式。
可以将密钥存储为明文或使用 OpenSSL 支持的任何算法(例如 AES-128)进行加密。如果密钥被存储为加密格式,则可以在 sslpassword 连接选项中提供密码。如果提供了加密密钥并且 sslpassword
选项缺失或为空白,那么 OpenSSL 将通过 Enter PEM pass phrase:
提示以交互方式提示输入密码(如果能够使用 TTY)。应用程序可以通过提供其自己的密钥密码回调函数来覆盖客户端证书提示和 sslpassword
参数的操作;请参阅 PQsetSSLKeyPassHook_OpenSSL
。
有关创建证书的说明,请参阅 第 18.9.5 节。
使用 sslmode
参数的不同值可以提供不同的保护级别。SSL 提供针对三种类型的攻击的保护
如果第三方可以查看客户端和服务器之间的网络通信量,它就可以读取连接信息(包括用户名和密码)以及所传递的数据。SSL使用加密对其进行阻止。
如果第三方可以在客户端和服务器之间的数据传输过程中修改数据,它就可以假装自己是该服务器,从而可以看到并修改数据 即便该数据已经加密。该第三方随后可以将连接信息和数据转发至原始服务器,从而使检测该攻击变得不可能。实现此目的的常见方法包括 DNS 欺骗和地址劫持,由此,客户端会将连接指向到非预期的一台不同的服务器。还有其他一些可用来实现此目的攻击方法。SSL通过验证证书对服务器进行认证,从而防止这种类型的攻击。
如果第三方可以假装成已授权客户端,它就可以轻松访问它无权访问的数据。这种情况通常因密码管理不安全而发生。SSL使用客户端证书可以防止这种情况发生,因为它可以确保只有持有有效证书的用户才能访问该服务器。
对于一个连接被已知为 SSL 安全的连接,必须在 客户端和服务器两端 配置 SSL 使用情况,然后才能进行连接。如果只在服务器上进行配置,则在客户端知道服务器的要求很高的安全性之前,该客户端可能就会发送敏感信息(例如,密码)。在 libpq 中,可以通过将 sslmode
参数设置为 verify-full
或 verify-ca
,并为系统提供一个用于进行验证的根证书来确保安全连接。这类似于使用一个 https
URL进行加密的网络浏览。
一旦对服务器进行认证,客户端就可以传递敏感数据。这意味着,在此时之前,该客户端无需知道是否会使用证书进行身份认证,因此,只需在服务器配置中指定这一点即可。
所有SSL选项都以加密和密钥交换的形式产生开销,因此,必须在性能和安全性之间进行权衡。 表 32.1 说明了不同 sslmode
值保护的风险以及它们对安全性和开销的说明。
表 32.1 SSL 模式说明
sslmode |
窃听保护 | MITM保护 | 说明 |
---|---|---|---|
disable |
否 | 否 | 我不关心安全,也不想支付加密开销。 |
允许 |
可能 | 否 | 我不关心安全,如果服务器要求的话,我将支付加密开销。 |
优先 |
可能 | 否 | 我不介意加密,如果服务器支持的话,我希望支付加密开销。 |
需要 |
是 | 否 | 我希望数据加密,并且接受开销。我相信该网络将确保我始终连接到我想要连接的服务器。 |
验证-ca |
是 | 取决于 CA 策略 | 我希望数据加密,并且接受开销。我希望确保我连接到值得信任的服务器。 |
验证-完整 |
是 | 是 | 我希望数据加密,并且接受开销。我希望确保我连接到我信任的服务器,并且这是我指定的服务器。 |
verify-ca
和 verify-full
之间存在的区别取决于根的策略CA。如果使用公共CA,则 verify-ca
允许连接到该服务器,其他人 可能已使用CA注册了该服务器。在这种情况下,始终应使用 verify-full
。如果使用本地CA,甚至使用自签名证书,使用 verify-ca
通常提供了足够的保护。
sslmode
的默认值为 prefer
。如表所示,从安全角度来看,这毫无意义,并且只有可能才会带来性能开销。它仅作为向后兼容性的默认设置提供,不推荐在安全部署中使用。
表 32.2 总结了与客户端 SSL 设置相关的文件。
表 32.2。Libpq/客户端 SSL 文件使用
文件 | 内容 | 效果 |
---|---|---|
~/.postgresql/postgresql.crt |
客户端证书 | 已发送到服务器 |
~/.postgresql/postgresql.key |
客户端私钥 | 证明所有者发送的客户端证书;并不表示证书所有者值得信任 |
~/.postgresql/root.crt |
受信任的证书颁发机构 | 检查服务器证书是否由受信任的证书颁发机构签名 |
~/.postgresql/root.crl |
被证书颁发机构吊销的证书 | 服务器证书不得在此列表中 |
如果应用程序初始化了 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中添加的,因此,对于需要与较旧版本的libpq配合使用的应用程序来说,PQinitSSL
可能是更可取的选择。