Overview
En esta guía, podrá aprender a utilizar el ProtocoloTLS para proteger su conexión a una implementación de MongoDB.
Cuando habilita TLS para una conexión, PyMongo realiza las siguientes acciones:
Utiliza TLS para conectarse a la implementación de MongoDB
Verifica el certificado de la implementación
Asegura que el certificado certifique la implementación
Para saber cómo configurar su implementación de MongoDB para TLS, consulte la guía de configuración de TLS en el manual de MongoDB Server.
Importante
Una descripción completa de los certificados TLS/SSL, PKI (Infraestructura de Clave Pública) y las Autoridades de Certificación (CA) queda fuera del alcance de este documento. Esta página presupone conocimientos previos de TLS/SSL y acceso a certificados válidos.
Habilitar TLS
Para habilitar TLS para la conexión a su instancia de MongoDB, configure la tls Opción de conexión a True. Puede hacerlo de dos maneras: pasando un argumento al constructor MongoClient o mediante un parámetro en su cadena de conexión.
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")
Tip
Si su cadena de conexión incluye la modificación +srv, que especifica el formato de conexión SRV, TLS estará habilitado en su conexión de manera predeterminada.
Para obtener más información sobre el formato de conexión SRV, consulte Formato de conexión SRV en la documentación de MongoDB Server.
Specify a CA File
Durante el protocolo de enlace TLS, la implementación de MongoDB presenta un archivo de clave de certificado a su aplicación para establecer su identidad. Normalmente, el certificado de una implementación ha sido firmado por una CA reconocida, y su aplicación depende de esta CA para validarlo.
Sin embargo, durante las pruebas es posible que quieras actuar como tu propia CA. En este caso, debes indicar a PyMongo que use tus certificados CA en lugar de los firmados por otra CA.
Para ello, utilice la opción de conexión tlsCAFile para especificar la ruta a un archivo .pem que contenga la cadena del certificado raíz. Puede hacerlo de dos maneras: pasando un argumento al constructor MongoClient o mediante un parámetro en la cadena de conexión.
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)
Comprobar revocación de certificado
Cuando un certificado X.509 deja de ser confiable (por ejemplo, si su clave privada se ha visto comprometida), la CA lo revoca. PyMongo ofrece dos maneras de comprobar si el certificado de un servidor ha sido revocado.
OCSP
Para utilizar el Protocolo de estado de certificado en línea (OCSP) para validar un certificado de servidor, debe instalar PyMongo con la opción ocsp, como se muestra en el siguiente ejemplo:
python -m pip install pymongo[ocsp]
El proceso de validación del certificado varía según la versión de MongoDB Server a la que se esté conectando:
MongoDB v4.4 o posterior: El servidor envía una respuesta OCSP con marca de tiempo a su certificado. PyMongo valida el certificado con la respuesta OCSP. Si la CA ha revocado el certificado o si la respuesta OCSP no es válida por cualquier otro motivo, el protocolo de enlace TLS falla.
MongoDB v4.3 o anterior: El servidor proporciona un punto final OCSP, con el que PyMongo contacta directamente. PyMongo valida el certificado con la respuesta OCSP. Si la CA no ha revocado el certificado, el protocolo de enlace TLS continúa, incluso si la respuesta OCSP no es válida o tiene un formato incorrecto.
Para evitar que PyMongo contacte con el punto final de OCSP, configure la opción de conexión tlsDisableOCSPEndpointCheck en True. Puede hacerlo de dos maneras: pasando un argumento al constructor MongoClient o mediante un parámetro en su cadena de conexión.
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)
Nota
Incluso si la opción tlsDisableOCSPEndpointCheck está establecida en True, PyMongo aún verifica cualquier respuesta OCSP adjunta al certificado de un servidor.
Lista de revocación de certificados
En lugar de usar OCSP, puede indicar a PyMongo que compare el certificado del servidor con una Lista de Revocación de Certificados (CRL) publicada por la CA. Para ello, utilice la opción de conexión tlsCRLFile para especificar la ruta a un archivo .pem o .der de la CA. Puede hacerlo de dos maneras: pasando un argumento al constructor MongoClient o mediante un parámetro en la cadena de conexión.
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)
Nota
No se puede utilizar una CRL y una OCSP en el mismo protocolo de enlace TLS.
Presentar un Certificado de Cliente
Algunas implementaciones de MongoDB requieren que cada aplicación que se conecta presente un certificado de cliente que acredite su identidad. Para especificar el certificado de cliente que PyMongo debe presentar, configure la opción tlsCertificateKeyFile con la ruta del archivo .pem que contiene su certificado y clave privada. Puede hacerlo de dos maneras: pasando un argumento al constructor MongoClient o mediante un parámetro en su cadena de conexión.
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)
Importante
Su certificado de cliente y su clave privada deben estar en el mismo archivo .pem. Si están almacenados en archivos diferentes, debe concatenarlos. El siguiente ejemplo muestra cómo concatenar un archivo de clave y un archivo de certificado en un tercer archivo llamado combined.pem en un sistema Unix:
cat key.pem cert.pem > combined.pem
Proporcionar una contraseña clave
Si la clave privada de su archivo de certificado está cifrada, debe proporcionar una contraseña. Para ello, utilice la opción de conexión tlsCertificateKeyFilePassword para especificar la contraseña o frase de contraseña de la clave privada cifrada. Puede hacerlo de dos maneras: pasando un argumento al constructor MongoClient o mediante un parámetro en su cadena de conexión.
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)
Permitir TLS inseguro
Cuando TLS está habilitado, PyMongo verifica automáticamente el certificado que presenta el servidor. Al probar su código, puede deshabilitar esta verificación. Esto se conoce como TLS inseguro.
Cuando TLS inseguro está activado, PyMongo solo requiere que el servidor presente un certificado X.509. El controlador acepta un certificado aun si se cumple alguna de las siguientes condiciones:
El nombre de host del servidor y el nombre del sujeto (o nombre alternativo del sujeto) en el certificado no coinciden.
El certificado está caducado o aún no es válido.
El certificado no tiene un certificado raíz confiable en la cadena.
El propósito del certificado no es válido para la identificación del servidor.
Nota
Incluso cuando está habilitado TLS inseguro, la comunicación entre el cliente y el servidor está cifrada con TLS.
Para habilitar TLS inseguro, configure la opción de conexión tlsInsecure en True. Puede hacerlo de dos maneras: pasando un argumento al constructor MongoClient o mediante un parámetro en su cadena de conexión.
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)
Para deshabilitar solo la validación del certificado, configure la opción tlsAllowInvalidCertificates en True y configure la opción tlsInsecure en False u omítala:
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)
Para deshabilitar solo la verificación del nombre de host, configure la opción tlsAllowInvalidHostnames en True y configure la opción tlsInsecure en False u omítala:
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)
Advertencia
No utilizar en producción
Establezca siempre las opciones tlsInsecure, tlsAllowInvalidCertificates y tlsAllowInvalidHostnames en False en producción.
Establecer cualquiera de estas opciones en True en un entorno de producción hace que su aplicación sea insegura y potencialmente vulnerable a certificados vencidos y a procesos externos que se hacen pasar por instancias de cliente válidas.
Solución de problemas
CERTIFICADO_VERIFICACIÓN_FALLÓ
Un mensaje de error similar al siguiente significa que OpenSSL no pudo verificar el certificado del servidor:
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
Esto sucede a menudo porque OpenSSL no puede acceder a los certificados raíz del sistema o porque los certificados están desactualizados.
Si usa Linux, asegúrese de tener instaladas las últimas actualizaciones del certificado raíz de su proveedor de Linux.
Si usa macOS y está ejecutando Python v3.7 o posterior que descargó de python.org, ejecute el siguiente comando para instalar certificados raíz:
open "/Applications/Python <YOUR PYTHON VERSION>/Install Certificates.command"
Tip
Para obtener más información sobre este problema, consulte el problema de 29065 Python.
Si usa portable-pypy, podría necesitar configurar una variable de entorno para indicar a OpenSSL dónde encontrar los certificados raíz. El siguiente ejemplo de código muestra cómo instalar el módulo certifi desde PyPi y exportar la SSL_CERT_FILE variable de entorno:
$ pypy -m pip install certifi $ export SSL_CERT_FILE=$(pypy -c "import certifi; print(certifi.where())")
VERSIÓN DEL PROTOCOLO DE ALERTA TLSV1
Un mensaje de error similar al siguiente significa que la versión de OpenSSL utilizada por Python no admite un protocolo TLS lo suficientemente nuevo para conectarse al servidor:
[SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version
Las mejores prácticas de la industria recomiendan, y algunas regulaciones exigen, que se deshabiliten los protocolos TLS antiguos en algunas implementaciones de MongoDB. Algunas implementaciones podrían deshabilitar TLS 1.0, mientras que otras podrían deshabilitar TLS 1.0 y TLS 1.1.
No se requieren cambios en la aplicación para que PyMongo use las versiones más nuevas de TLS, pero algunas versiones de sistemas operativos podrían no proporcionar una versión de OpenSSL lo suficientemente nueva como para soportarlas.
Si usa macOS v10.12 (High Sierra) o anterior, instale Python desde python.org, homebrew, macports o una fuente similar.
Si usa Linux u otro Unix que no sea macOS, utilice el siguiente comando para comprobar su versión de OpenSSL:
openssl version
Si el comando anterior muestra un número de versión inferior a 1.0.1, la compatibilidad con TLS 1.1 o posterior no está disponible. Actualice a una versión más reciente o contacte con el proveedor de su sistema operativo para obtener una solución.
Para comprobar la versión TLS de su intérprete de Python, instale el módulo requests y ejecute el siguiente código:
python -c "import requests; print(requests.get('https://www.howsmyssl.com/a/check', verify=False).json()['tls_version'])"
Debería ver TLS 1.1 o posterior.
Respuesta de estado no válida
Un mensaje de error similar al siguiente significa que la verificación de revocación del certificado falló:
[('SSL routines', 'tls_process_initial_server_flight', 'invalid status response')]
Para obtener más detalles, consulta la sección OCSP de esta guía.
SSLV3_ALERTA_FALLO_DE_CONTROL_DE_MANOS
Al utilizar Python v3.10 o posterior con versiones de MongoDB anteriores a la v4.0, puedes ver errores similares a los siguientes mensajes:
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)
Los registros del servidor MongoDB también podrían mostrar el siguiente error:
2021-06-30T21:22:44.917+0100 E NETWORK [conn16] SSL: error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher
Cambios realizados en el módulo SSL en Python v3.10 podría causar incompatibilidades con versiones de MongoDB anteriores a la v4.0. Para resolver este problema, intenta uno o más de los siguientes pasos:
Degradar Python a v3.9 o anterior
Actualizar MongoDB Server a v4.2 o posterior
Instale PyMongo con la opción OCSP, que se basa en PyOpenSSL
Renegociación de legado inseguro deshabilitada
Al utilizar OpenSSL v3 o posterior, es posible que vea un error similar al siguiente mensaje:
[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled
Este tipo de errores ocurren debido a servidores proxy SSL obsoletos o con errores que aplican por error la renegociación de TLS heredada.
Para resolver este problema, realice los siguientes pasos:
Utilice la UnsafeLegacyServerConnect opción
Cree un archivo de configuración que incluya la opción UnsafeLegacyServerConnect. El siguiente ejemplo muestra cómo configurar la opción UnsafeLegacyServerConnect:
openssl_conf = openssl_init [openssl_init] ssl_conf = ssl_sect [ssl_sect] system_default = system_default_sect [system_default_sect] Options = UnsafeLegacyServerConnect
Importante
Dado que configurar la UnsafeLegacyServerConnect opción tiene implicaciones de seguridad, utilice esta solución alternativa como último recurso para abordar los unsafe legacy renegotiation disabled errores.
Documentación de la API
Para obtener más información sobre cómo configurar TLS para PyMongo, consulte la siguiente documentación de API: