PostgreSQL 9.4.4 中文手册
上一页 | 上一级 | 章 31. libpq - C 库 | 下一页

# 31.18. SSL 支持

PostgreSQL本机支持使用SSL连接对客户端/服务器通讯进行加密， 以增强安全性。参阅第 17.9 节获取服务器端SSL功能的细节。

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`(Common Name)属性与主机名进行匹配。 如果`cn`以`*`开始，会被看做是一个通配符，并且会匹配除了 点（`.`）之外的所有字符。这就意味着认证不会匹配子域名。 如果是使用IP而不是主机名进行连接，会进行IP匹配检查（不会做DNS检查）。

为了允许服务器认证通过，一个或多个信任的CA认证必须放在用户的home目录下的 `~/.postgresql/root.crt`文件中。如果中介CA 出现在`root.crt`中，该文件必须也包含到它们的根CA 的证书链。Windows下的文件名是`%APPDATA%\postgresql\root.crt`。

如果存在`~/.postgresql/root.crl`文件（Windows下是 `%APPDATA%\postgresql\root.crl`文件），同样也会检查证书吊销列表（CRL）。

root认证文件和CRL的位置可以通过设置`sslrootcert`和`sslcrl`连接参数， 或`PGSSLROOTCERT`和`PGSSLCRL`环境变量进行修改。

> 注意: 为了与早期PostgreSQL版本兼容，如果存在一个根CA文件，`sslmode`=`require` 的行为将和`verify-ca`表现的相同，意味着服务器证书是经过CA验证的。 不建议依赖于这个行为，需要证书验证的应用应该总是使用`verify-ca` 或`verify-full`。

## 31.18.2. 客户端证书

如果服务器要求一个信任的客户端认证，libpq将发送存储在用户home目录中 `~/.postgresql/postgresql.crt`文件中的证书。该证书必须由服务器信任的证书认证 （CA）之一签名。同时也必须出示一个匹配的私钥文件 `~/.postgresql/postgresql.key`。私钥文件不允许任何对世界或组的访问； 通过`chmod 0600 ~/.postgresql/postgresql.key`命令可以实现。 在Windows上，这个文件是`%APPDATA%\postgresql\postgresql.crt` 和`%APPDATA%\postgresql\postgresql.key`，同时没有特定的权限检查， 因为目录被认为是安全的。证书和key文件的位置可以通过`sslcert`和`sslkey`连接参数， 或`PGSSLCERT`和`PGSSLKEY`环境变量进行覆盖重写。

在一些情况下，客户端认证可能会被一个"intermediate"的证书认证来签名， 而不是一个能直接被服务信任的。为了使用一个这种认证，向`postgresql.crt` 文件追加证书签字权，并且直到认证授权，"root"或"intermediate"， 都可以被服务器信任，也就是在服务器的`root.crt` 文件中通过证书签约。

需要注意的是，客户端的`~/.postgresql/root.crt`列出了最高级别的CA， 认为对签名服务证书来说是可信任的。原则上，不需要列出签名客户端认证的CA， 尽管在大多数情况下，CA仍会被认为对服务器认证是可信任的。

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

`sslmode`参数的不同值提供了不同的保护级别。SSL可以为三种攻击提供保护：

Eavesdropping（窃听）

  如果一个第三方可以在客户端与服务器端之间检查网络通信，那么它就能读取两边的连接信息 （包括用户名和密码）以及传递的数据。对此，SSL通过加密进行防护。

裁判(MITM)

  如果客户端和服务器端进行传递数据的时候，第三方可以对其进行修改，那么他就能伪装成服务器， 然后查看或修改数据（即使是加密的）。第三方接着可以向原始服务器发出连接信息和数据， 最终造成无法防护这种攻击。这种攻击常用的载体有DNS中毒或IP绑架， 即客户端被定向到预期之外的不同的服务器。同样还有几种其他的方法也能做到这种攻击。 SSL通过服务器到客户端的证书验证来阻止这种攻击。

Impersonation（模拟）

  如果第三方可以伪装成一个认证了的客户端，那么它就能轻松访问到它本来不能访问的数据。 典型的，如不安全的密钥管理，就会造成这种情况。SSL 通过客户端认证来阻止这种情况，即确保只有知道有效认证的人员才能访问连接服务器。

对于一个被称为安全的连接来说，进行连接之前，必须在客户端和服务器端都进行SSL配置。 如果只在服务器端进行配置，在它知道服务器端需要高级认证之前不会发送敏感信息（如密码等）。 在libpq中，可以通过将`sslmode`参数设置为`verify-full`或`verify-ca` 来确保安全连接，并且为系统提供一个root认证以进行安全认证。类似于使用`https` 和URL进行加密网页浏览。

一旦服务器已经认证，客户端就可以发送敏感信息。这就意味着直到这一刻，客户端都不需要知道， 是否认证需要证书，只在服务器配置，对其安全地指定。

所有以加密和密钥交换方式得SSL选项都会产生开销，因此在性能和安全之间需要进行一个权衡。 表 31-1说明不同`sslmode`值的安全风险， 以及关于安全和开销所做出的声明：

表 31-1. SSL 模式说明

`sslmode` | 窃听保护 | MITM保护 | 声明
--- | --- | --- | ---
`disable` | 否 | 否 | 我不关心安全，我不想来支付加密的开销。
`allow` | 可能 | 否 | 我不关心安全性，但我会支付的加密开销，如果服务器的坚持的话。
`prefer` | 可能 | 否 | 我不关心加密，但我想支付加密开销，如果服务器支持它。
`require` | 是 | 否 | 我希望我的数据加密，我接受开销。我相信该网络将确保我始终连接到我想要的服务器。
`verify-ca` | 是 | `取决于CA`的政策 | 我希望我的数据加密，我接受开销。我想要确保我连接到了一个我信任的服务器。
`verify-full` | 是 | 是 | 我希望我的数据加密，我接受开销。我想要确保我连接到了一个我信任的服务器， 并且是我指定的那个服务器。

`verify-ca`和`verify-full`之间的不同是根据root CA的政策。 如果使用的是一个公用CA，`verify-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 库初始化

如果应用程序初始化`libcrypto`和/或`libssl`库以及libpq 编译为支持SSL，应该调用`PQinitOpenSSL`来告诉libpq 说`libssl`和/或`libcrypto`库已经被应用程序初始化了， 因此libpq将不会再初始化这些库。参阅 http://h71000.www7.hp.com/doc/83final/BA554_90007/ch04.html 获取关于SSL API的详细信息。

`PQinitOpenSSL`

  允许应用程序选择安全库初始化。

      void PQinitOpenSSL(int do_ssl, int do_crypto);

  当`do_ssl`为非0时，在第一次打开一个数据库连接之前，libpq 将初始化OpenSSL库。当`do_crypto`为非0时，`libcrypto` 库将被初始化。缺省(如果`PQinitOpenSSL`没有被调用)，两个库都会被初始化。 如果没有编译SSL支持，会提供该函数，但不会做任何事情。

  如果应用程序使用并初始化OpenSSL，或其底层`libcrypto`库， 那么在第一次打开一个数据库连接之前，必须调用这个函数（带有适当0值的参数）。 同样要确保在打开一个数据库连接之前做过初始化。

`PQinitSSL`

  允许应用程序选择初始化哪个安全库。

      void PQinitSSL(int do_ssl);

  此功能相当于`PQinitOpenSSL(do_ssl, do_ssl)`。 它的应用是足够的同时初始化或都不初始化OpenSSL和`libcrypto`。

  `PQinitSSL`在PostgreSQL 8.0就已经出现了， 而`PQinitOpenSSL`是在PostgreSQL 8.4添加进来的。 所以对老版本的libpq的使用，`PQinitSSL`是对应用程序的不错的选择。

上一页 | 起始页 | 下一页
LDAP查找连接参数 | 上一级 | 在多线程程序里的行为