Overview
在本指南中,您可以学习;了解如何使用TLS协议保护与MongoDB 部署的连接。
为连接启用 TLS 时,PyMongo 会执行以下操作:
使用 TLS 连接到 MongoDB 部署
验证部署的证书
确保证书证明部署
要学习;了解如何为 TLS 配置MongoDB 部署,请参阅MongoDB Server手册中的TLS 配置指南。
重要
TLS/SSL、PKI(公钥基础设施)证书和证书颁发机构 (CA) 的完整描述超出了本文档的范围。 本页假设您已了解 TLS/SSL 并可访问有效证书。
启用 TLS
要为与MongoDB实例的连接启用TLS,请将tls连接选项设立为True 。 您可以通过两种方式执行此操作:将参数传递给 MongoClient 构造函数,或通过连接string中的参数。
client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname:<port>", tls=True)
client = pymongo.MongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>?tls=true")
client = pymongo.AsyncMongoClient("mongodb://<db_username>:<db_password>@<hostname:<port>", tls=True)
client = pymongo.AsyncMongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>?tls=true")
提示
如果您的连接string包含 +srv 修饰符(指定 SRV 连接格式),则默认情况下会对您的连接启用 TLS。
要学习;了解有关 SRV 连接格式的更多信息,请参阅MongoDB Server文档中的SRV 连接格式。
指定 CA 文件
在 TLS 握手期间,MongoDB 部署会向您的应用程序提供证书密钥文件,以确定其身份。 通常,部署的证书由知名 CA 签名,并且您的应用程序依赖此 CA 来验证证书。
但是,在测试过程中,您可能希望充当自己的 CA。 在这种情况下,您必须指示 PyMongo 使用您的 CA 证书,而不是其他 CA 签名的证书。
为此,请使用tlsCAFile连接选项指定包含根证书链的.pem文件的路径。 您可以通过两种方式执行此操作:将参数传递给 MongoClient 构造函数,或通过连接string中的参数。
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)
client = pymongo.AsyncMongoClient("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.AsyncMongoClient(uri)
检查证书撤销
当 X. 509证书不再可信时(例如,如果其私钥已泄露),CA 将撤销该证书。 PyMongo 提供两种检查服务器证书是否已被撤销的方法。
OCSP
要使用在线证书状态协议 (OCSP) 验证服务器证书,必须使用ocsp选项安装 PyMongo,如以下示例所示:
python -m pip install pymongo[ocsp]
证书验证过程因您要连接的 MongoDB Server 版本而异:
MongoDB v 4.4或更高版本:服务器将带时间戳的 OCSP 响应装订到其证书中。 PyMongo 根据 OCSP 响应验证证书。 如果 CA 已撤销证书,或者 OCSP 响应无效,则 TLS 握手失败。
MongoDB v 4.3或更早版本:服务器提供 PyMongo 直接联系的 OCSP 端点。 然后,PyMongo 根据 OCSP 响应验证证书。 如果 CA 尚未撤销证书,则即使 OCSP 响应无效或格式不正确,TLS 握手也会继续。
注意
Let's Encrypt OCSP 弃用
Let's Encrypt 已终止对较新证书的 OCSP支持。 Let's Encrypt 颁发的新证书可能不包含 OCSP 响应程序URL。仅当证书包含 OCSP URI 时,才会进行 OCSP 吊销检查,因此缺少 OCSP URL是正常情况,并不一定表示MongoDB 部署有问题。
要阻止 PyMongo 联系 OCSP 端点,请将tlsDisableOCSPEndpointCheck连接选项设置为True 。 您可以通过两种方式执行此操作:将参数传递给 MongoClient 构造函数,或通过连接string中的参数。
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)
client = pymongo.AsyncMongoClient("mongodb://<db_username>:<db_password>@<hostname>:<port>", tls=True, tlsDisableOCSPEndpointCheck=True)
uri = "mongodb://example.com/?tls=true&tlsDisableOCSPEndpointCheck=true" client = pymongo.AsyncMongoClient(uri)
注意
即使将tlsDisableOCSPEndpointCheck选项设置为True ,PyMongo 仍会验证装订到服务器证书的任何 OCSP 响应。
证书吊销列表
您可以指示 PyMongo 根据 CA 发布的证书吊销列表 (CRL) 检查服务器的证书,而不是使用 OCSP。 为此,请使用tlsCRLFile连接选项指定 CA 中的.pem或.der文件的路径。 您可以通过两种方式执行此操作:将参数传递给 MongoClient 构造函数,或通过连接string中的参数。
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)
client = pymongo.AsyncMongoClient("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.AsyncMongoClient(uri)
注意
您不能在同一次 TLS 握手中同时使用 CRL 和OCSP 。
出示客户端证书
某些 MongoDB 部署要求每个连接的应用程序提供证明其身份的客户端证书。 要指定 PyMongo 显示的客户端证书,请将tlsCertificateKeyFile选项设置为包含证书和私钥的.pem文件的文件路径。 您可以通过两种方式执行此操作:将参数传递给 MongoClient 构造函数,或通过连接string中的参数。
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)
client = pymongo.AsyncMongoClient("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.AsyncMongoClient(uri)
重要
客户端证书和私钥必须位于同一.pem文件中。 如果它们存储在不同的文件中,则必须将它们连接起来。 以下示例展示了如何在 Unix 系统上将密钥文件和证书文件连接到名为combined.pem的第三个文件中:
cat key.pem cert.pem > combined.pem
提供密钥密码
如果证书文件中的私钥已加密,则必须提供密码。 为此,请使用tlsCertificateKeyFilePassword连接选项指定加密私钥的密码或口令。 您可以通过两种方式执行此操作:将参数传递给 MongoClient 构造函数,或通过连接string中的参数。
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)
client = pymongo.AsyncMongoClient("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.AsyncMongoClient(uri)
允许不安全的 TLS
启用 TLS 后,PyMongo 会自动验证服务器提供的证书。 测试代码时,可以禁用此验证。 这称为不安全的 TLS。
启用不安全 TLS 时,PyMongo 仅要求服务器提供 X. 509证书。 即使满足以下任一条件,驱动程序也会接受证书:
服务器的主机名与证书上的主题名称(或主题备用名称)不匹配。
证书过期或无效。
证书链中没有受信任的根证书。
证书用途对服务器标识无效。
注意
即使启用了不安全的 TLS,客户端和服务器之间的通信也会使用 TLS 进行加密。
要启用不安全的 TLS,请将tlsInsecure连接选项设置为True 。 您可以通过两种方式执行此操作:将参数传递给 MongoClient 构造函数,或通过连接string中的参数。
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)
client = pymongo.AsyncMongoClient("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.AsyncMongoClient(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)
client = pymongo.AsyncMongoClient("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.AsyncMongoClient(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)
client = pymongo.AsyncMongoClient("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.AsyncMongoClient(uri)
警告
不要在生产中使用
在生产环境中,始终将tlsInsecure 、 tlsAllowInvalidCertificates和tlsAllowInvalidHostnames选项设置为False 。
在生产环境中将这些选项中的任何一个设置为True都会导致应用程序不安全,并且可能容易受到过期证书和冒充有效客户端实例的外部进程的攻击。
故障排除
CERTIFICATE_VERIFY_FAILED
类似以下的错误消息表示 OpenSSL 无法验证服务器的证书:
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
发生这种情况的常见原因是 OpenSSL 无法访问系统的根证书,或者证书已过期。
如果使用 Linux,请确保安装了 Linux 供应商提供的最新根证书更新。
如果您使用 macOS,并且运行的是从 python.org 下载的 Python v 3.7或更高版本, 运行以下命令安装根证书:
open "/Applications/Python <YOUR PYTHON VERSION>/Install Certificates.command"
提示
有关此问题的更多信息,请参阅Python问题 29065。
如果使用portable-pypy,则可能需要设立一个环境变量来告诉 OpenSSL 在哪里可以找到根证书。以下代码示例展示了如何从 PyPi 安装certifi 模块并导出 SSL_CERT_FILE 环境变量:
$ pypy -m pip install certifi $ export SSL_CERT_FILE=$(pypy -c "import certifi; print(certifi.where())")
TLSV 1 _ALERT_PROTOCOL_VERSION
类似以下的错误消息表示 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 v 10.12 (High Sierra) 或更早版本,请从 python.org 安装 Python, Homebrew、macports 或类似来源。
如果您使用 Linux 或其他非 macOS Unix,请使用以下命令检查 OpenSSL 版本:
openssl version
如果前面的命令显示的版本号小于1.0.1 , 不支持 TLS 1.1或更高版本。 升级到较新版本或联系操作系统供应商以获取解决方案。
要检查 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部分。
SSLV 3 _ALERT_HANDSHAKE_FAILURE
将 Python v 3.10或更高版本与 v 4.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 Server 日志还可能显示以下错误:
2021-06-30T21:22:44.917+0100 E NETWORK [conn16] SSL: error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher
对Python v3.10 中的 ssl 模块进行的更改可能会导致与 v4.0 之前的MongoDB版本不兼容。要解决此问题,请尝试以下一个或多个步骤:
将 Python 降级到 v 3.9或更早版本
将 MongoDB Server 升级到 v 4.2或更高版本
使用OCSP选项安装 PyMongo,该选项依赖于 PyOpenSSL
已禁用不安全传统重新协商
使用 OpenSSL v 3 或更高版本时,您可能会看到类似以下消息的错误:
[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled
发生这些类型的错误是由于过时或有缺陷的 SSL 代理错误地实施了传统TLS 重新协商。
要解决此问题,请执行以下步骤:
重要
由于设置 UnsafeLegacyServerConnect 选项存在安全隐患,因此请将此解决方法作为解决unsafe legacy renegotiation disabled 错误的最后手段。
API 文档
要了解有关为 PyMongo 配置 TLS 的更多信息,请参阅以下 API 文档: