Mensaje de error
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'api.example.com'.
También se muestra como
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for '127.0.0.1'.
El nombre entre comillas es el host al que te conectas. Para direcciones IP, Python dice «IP address mismatch, certificate is not valid for '…'.» Tras el mensaje aparece una ubicación en el código fuente, como «(_ssl.c:1082)».
Python ssl module (OpenSSL host check) — ssl.SSLCertVerificationError, X509_V_ERR_HOSTNAME_MISMATCH (62)
Qué significa
La cadena de certificados es de confianza, pero el nombre que se pidió a Python que verificara (el host de la URL o server_hostname) no está entre los nombres alternativos del sujeto del certificado. Desde Python 3.7, la comprobación la hace OpenSSL durante el handshake, así que aparece como un fallo de verificación del certificado y no como el antiguo ssl.CertificateError.
Por qué ocurre
El certificado no incluye este nombre de host
frecuenteEl certificado cubre otros nombres (www., el dominio principal, un nombre de servicio antiguo). Los comodines cubren exactamente una etiqueta, así que *.example.com coincide con api.example.com, pero no con example.com ni con a.b.example.com.
Conexión por dirección IP, localhost o un alias de contenedor
frecuenteLa URL usa 127.0.0.1, una IP privada, «localhost» o un nombre de servicio de docker-compose, y ninguno de ellos aparece en un certificado emitido para un nombre DNS público.
El servidor devolvió el certificado de otro sitio
ocasionalLos hosts compartidos eligen el certificado según el nombre SNI; un nombre sin configuración propia recibe el certificado predeterminado.
Cómo solucionarlo
- 1.
Listar los nombres que cubre el certificado
Compara las entradas de subjectAltName con el host que pasas a Python.
bash echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \ | openssl x509 -noout -subject -ext subjectAltName - 2.
Volver a emitir con todos los nombres en subjectAltName
Añade todos los nombres DNS y, para el acceso por IP, entradas IP. Los nombres que solo figuran en el nombre común (CN) no se usan para la comparación.
bash # Include every DNS name and IP address clients use openssl req -new -key server.key -out server.csr -subj "/CN=api.example.com" \ -addext "subjectAltName=DNS:api.example.com,DNS:api.internal.example.com,IP:10.0.0.5" - 3.
Verificar el nombre del certificado al conectarse a una dirección
Cuando tengas que llegar a una dirección concreta (un nodo de un balanceador de carga, un túnel), mantén server_hostname con el nombre que figura en el certificado en lugar de desactivar la comprobación del nombre de host.
python import socket, ssl # Connect to an address, but verify (and send SNI) for the certificate's name ctx = ssl.create_default_context() with socket.create_connection(("10.0.0.5", 443)) as raw: with ctx.wrap_socket(raw, server_hostname="api.example.com") as tls: print(tls.version())
Errores relacionados
x509: certificate is valid for example.com, not api.example.comGo crypto/x509curl: (60) SSL: no alternative certificate subject name matches target hostname…curl (OpenSSL backend)Hostname/IP does not match certificate's altnames: Host: api.example.com. is no…Node.js tls.checkServerIdentity()Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1…requests / urllib3 (Python)