文档菜单
文档首页
/ / /
PyMongo
/

配置传输层安全性 (TLS)

本页内容

  • 概述
  • 启用 TLS
  • 指定 CA 文件
  • 检查证书吊销
  • OCSP
  • 证书吊销列表
  • 提供客户端证书
  • 提供密钥密码
  • 允许不安全的 TLS
  • TLS 故障排除
  • CERTIFICATE_VERIFY_FAILED
  • TLSV1_ALERT_PROTOCOL_VERSION
  • 无效状态响应
  • SSLV3_ALERT_HANDSHAKE_FAILURE
  • 禁用不安全的旧协商
  • API 文档

在本指南中,您可以学习如何使用TLS协议来保护您与MongoDB部署的连接。

当您为连接启用TLS时,PyMongo将执行以下操作

  • 使用TLS连接到MongoDB部署

  • 验证部署的证书

  • 确保证书验证了部署

有关如何配置MongoDB部署以使用TLS的说明,请参阅MongoDB服务器手册中的TLS配置指南

重要

关于TLS/SSL、PKI(公钥基础设施)证书和证书颁发机构(CA)的完整描述超出了本文档的范围。本页面假定您已了解TLS/SSL并有权访问有效的证书。

要为您的MongoDB实例连接启用TLS,请将tls连接选项设置为True。您可以通过两种方式完成此操作:通过传递参数给MongoClient构造函数或通过连接字符串中的参数。

client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname:<port>", tls=True)
client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>?tls=true")

提示

如果您的连接字符串包含 +srv 修改,该修改指定了 SRV 连接格式,则默认情况下您的连接启用了 TLS。

要了解有关 SRV 连接格式的更多信息,请参阅 MongoDB 服务器文档中的 SRV 连接格式

在 TLS 握手过程中,MongoDB 部署向您的应用程序提供一个证书密钥文件以建立其身份。通常,部署的证书已由知名 CA 签署,您的应用程序依赖于此 CA 来验证证书。

然而,在测试期间,您可能想充当自己的 CA。在这种情况下,您必须指示 PyMongo 使用您的 CA 证书而不是由其他 CA 签署的证书。

要这样做,请使用 tlsCAFile 连接选项来指定包含根证书链的 .pem 文件的路径。您可以通过两种方式完成此操作:通过传递参数给 MongoClient 构造函数或通过连接字符串中的参数。

client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>",
tls=True,
tlsCAFile="/path/to/ca.pem")
uri = "mongodb://<db_username>:<db_password>@<hostname>:<port>/?tls=true&tlsCAFile=/path/to/ca.pem"
client = pymongo.MongoClient(uri)

当 X.509 证书不再可信时(例如,如果其私钥已受损),CA 将吊销该证书。PyMongo 包含两种检查服务器证书是否已被吊销的方法。

要使用在线证书状态协议(OCSP)验证服务器证书,您必须使用具有 ocsp 选项安装 PyMongo,如下例所示

python -m pip install pymongo[ocsp]

证书验证过程取决于您连接到的 MongoDB 服务器版本

  • MongoDB v4.4 或更高版本:服务器将时间戳 OCSP 响应附加到其证书上。PyMongo 将证书与 OCSP 响应进行验证。如果证书已被证书颁发机构撤销,或者 OCSP 响应无效,TLS 握手将失败。

  • MongoDB v4.3 或更早版本:服务器提供 OCSP 终端,PyMongo 直接与该终端通信。然后 PyMongo 将证书与 OCSP 响应进行验证。如果证书未被证书颁发机构撤销,TLS 握手将继续进行--即使 OCSP 响应无效或格式不正确。

要阻止 PyMongo 请求 OCSP 终端,请将 tlsDisableOCSPEndpointCheck 连接选项设置为 True。您可以通过两种方式完成此操作:通过传递参数给 MongoClient 构造函数或通过连接字符串中的参数。

client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>",
tls=True,
tlsDisableOCSPEndpointCheck=True)
uri = "mongodb://example.com/?tls=true&tlsDisableOCSPEndpointCheck=true"
client = pymongo.MongoClient(uri)

注意

即使将 tlsDisableOCSPEndpointCheck 选项设置为 True,PyMongo 仍然会验证附加到服务器证书的任何 OCSP 响应。

您可以使用 tlsCRLFile 连接选项来指示 PyMongo 使用证书颁发机构发布的证书撤销列表(CRL)检查服务器的证书,而不是使用 OCSP。为此,请通过传递参数给 MongoClient 构造函数或通过连接字符串中的参数指定 .pem.der 文件的路径。

client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>",
tls=True,
tlsCRLFile="/path/to/crl.pem")
uri = "mongodb://example.com/?tls=true&tlsCRLFile=/path/to/crl.pem"
client = pymongo.MongoClient(uri)

注意

您不能在同一个 TLS 握手中同时使用 CRL 和 OCSP。

一些MongoDB部署要求每个连接的应用程序都展示一个客户端证书,以证明其身份。为了指定PyMongo展示的客户端证书,将tlsCertificateKeyFile选项设置为包含您的证书和私钥的.pem文件的文件路径。您可以通过两种方式完成此操作:通过将参数传递给MongoClient构造函数或通过连接字符串中的参数。

client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>",
tls=True,
tlsCertificateKeyFile='/path/to/client.pem')
uri = ("mongodb://<db_username>:<db_password>@<hostname:<port>/?"
"tls=true"
"&tlsCertificateKeyFile=path/to/client.pem")
client = pymongo.MongoClient(uri)

重要

您的客户端证书和私钥必须在同一个.pem文件中。如果它们存储在不同的文件中,您必须将它们连接起来。以下示例展示了如何在Unix系统上如何将密钥文件和证书文件连接到名为combined.pem的第三个文件中

$ cat key.pem cert.pem > combined.pem

如果您的证书文件中的私钥被加密,您必须提供密码。为此,使用tlsCertificateKeyFilePassword连接选项指定加密私钥的密码或密钥短语。您可以通过两种方式完成此操作:通过将参数传递给MongoClient构造函数或通过连接字符串中的参数。

client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname:<port>",
tls=True,
tlsCertificateKeyFile='/path/to/client.pem',
tlsCertificateKeyFilePassword=<passphrase>)
uri = ("mongodb://<db_username>:<db_password>@<hostname:<port>/?"
"tls=true"
"&tlsCertificateKeyFile=path/to/client.pem"
"&tlsCertificateKeyFilePassword=<passphrase>")
client = pymongo.MongoClient(uri)

当启用TLS时,PyMongo会自动验证服务器提供的证书。在测试代码时,您可以禁用此验证。这被称为不安全TLS。

当启用不安全TLS时,PyMongo仅要求服务器提供X.509证书。即使以下任何一项为真,驱动程序也会接受证书

  • 服务器的主机名和证书上的主题名称(或主题备用名称)不匹配。

  • 证书已过期或尚未生效。

  • 证书链中没有受信任的根证书。

  • 证书用途对服务器标识无效。

注意

即使启用了不安全TLS,客户端和服务器之间的通信也是通过TLS加密的。

要启用不安全TLS,将tlsInsecure连接选项设置为True。您可以通过两种方式做到这一点:通过将参数传递给MongoClient构造函数或通过连接字符串中的参数。

client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname:<port>",
tls=True,
tlsInsecure=True)
uri = ("mongodb://<db_username>:<db_password>@<hostname>:<port>/?"
"tls=true"
"&tlsInsecure=true")
client = pymongo.MongoClient(uri)

要仅禁用证书验证,将tlsAllowInvalidCertificates选项设置为True,并将tlsInsecure选项设置为False或省略它

client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>",
tls=True,
tlsAllowInvalidCertificates=True)
uri = ("mongodb://<db_username>:<db_password>@<hostname>:<port>/?"
"tls=true"
"&tlsAllowInvalidCertificates=true")
client = pymongo.MongoClient(uri)

要仅禁用主机名验证,将tlsAllowInvalidHostnames选项设置为True,并将tlsInsecure选项设置为False或省略它

client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>",
tls=True,
tlsAllowInvalidHostnames=True)
uri = ("mongodb://<db_username>:<db_password>@<hostname>:<port>/?"
"tls=true"
"&tlsAllowInvalidHostnames=true")
client = pymongo.MongoClient(uri)

警告

请勿在生产环境中使用

在生产环境中,始终将tlsInsecuretlsAllowInvalidCertificatestlsAllowInvalidHostnames选项设置为False

在生产环境中将这些选项中的任何一个设置为True会使您的应用程序不安全,并可能使应用程序容易受到过期证书和冒充有效客户端实例的外部进程的攻击。

以下类似错误信息表示OpenSSL无法验证服务器的证书

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

这通常是因为OpenSSL无法访问系统的根证书,或者因为证书已过时。

如果您使用Linux,请确保您已经从Linux供应商安装了最新的根证书更新。

如果您使用macOS,并且正在运行从python.org下载的Python 3.7或更高版本,请运行以下命令来安装根证书:

open "/Applications/Python <YOUR PYTHON VERSION>/Install Certificates.command"

提示

有关此问题的更多信息,请参阅Python问题29065。

如果您使用便携式pypy,可能需要设置一个环境变量来告诉OpenSSL根证书的位置。以下代码示例展示了如何从PyPi安装certifi模块并导出环境变量SSL_CERT_FILE

$ pypy -m pip install certifi
$ export SSL_CERT_FILE=$(pypy -c "import certifi; print(certifi.where())")

提示

有关此问题的更多信息,请参阅便携式pypy问题15。

类似以下错误消息表示Python使用的OpenSSL版本不支持足够新的TLS协议以连接到服务器

[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version

行业最佳实践推荐,某些法规要求,在某些MongoDB部署中禁用旧版TLS协议。某些部署可能禁用TLS 1.0,而其他部署可能禁用TLS 1.0和TLS 1.1。

PyMongo使用最新的TLS版本不需要进行应用程序更改,但某些操作系统版本可能不提供足够新的OpenSSL版本来支持它们。

如果您使用macOS v10.12(High Sierra)或更早版本,请从python.org、homebrew、macports或类似来源安装Python。

如果您使用Linux或其他非macOS Unix,请使用以下命令检查您的OpenSSL版本

$ openssl version

如果前面的命令显示的版本号小于1.0.1,则不支持TLS 1.1或更高版本的TLS。升级到较新版本或联系您的操作系统供应商以获取解决方案。

要检查Python解释器的TLS版本,请安装requests模块并执行以下代码

python -c "import requests; print(requests.get('https://www.howsmyssl.com/a/check', verify=False).json()['tls_version'])"

您应该看到TLS 1.1或更高版本。

类似以下错误消息表示证书吊销检查失败

[('SSL routines', 'tls_process_initial_server_flight', 'invalid status response')]

有关更多详细信息,请参阅本指南中的OCSP部分。

当使用Python 3.10或更高版本与低于v4.0版本的MongoDB时,您可能会看到以下类似的消息错误

SSL handshake failed: localhost:27017: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:997)
SSL handshake failed: localhost:27017: EOF occurred in violation of protocol (_ssl.c:997)

MongoDB服务器日志也可能显示以下错误

2021-06-30T21:22:44.917+0100 E NETWORK [conn16] SSL: error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher

Python 3.10中对ssl模块所做的更改可能与低于v4.0版本的MongoDB不兼容。要解决此问题,尝试以下步骤之一或多个

  • 将Python降级到v3.9或更早版本

  • 将MongoDB服务器升级到v4.2或更高版本

  • 使用带OCSP选项安装PyMongo,该选项依赖于PyOpenSSL

当使用OpenSSL v3或更高版本时,您可能会看到以下类似的消息错误

[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled

这些类型的错误是由于过时或有错误的SSL代理,错误地强制执行旧的TLS重协商。

要解决此问题,请执行以下步骤

1

运行以下命令以确保已安装OpenSSL v3.0.4或更高版本

openssl version
2

创建一个包含UnsafeLegacyServerConnect选项的配置文件。以下示例展示了如何设置UnsafeLegacyServerConnect选项

openssl_conf = openssl_init
[openssl_init]
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
Options = UnsafeLegacyServerConnect
3

设置OPENSSL_CONF环境变量以使用您刚刚创建的OpenSSL配置文件运行Python

OPENSSL_CONF=/path/to/the/config/file/above.cnf python ...

重要

由于设置 UnsafeLegacyServerConnect 选项存在安全影响[安全影响,请使用此解决方案作为解决“不安全的旧版协商已禁用”错误的最后手段。

要了解有关为 PyMongo 配置 TLS 的更多信息,请参阅以下 API 文档

返回

指定连接选项