Сообщение об ошибке
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'api.example.com'.
Также выводится как
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for '127.0.0.1'.
Имя в кавычках — хост, к которому вы подключались. Для IP-адресов Python пишет «IP address mismatch, certificate is not valid for '…'.». После сообщения указывается место в исходном коде, например «(_ssl.c:1082)».
Python ssl module (OpenSSL host check) — ssl.SSLCertVerificationError, X509_V_ERR_HOSTNAME_MISMATCH (62)
Что это означает
Цепочка сертификатов признана доверенной, но имени, которое Python должен был проверить (хост из URL или server_hostname), нет среди альтернативных имён субъекта (SAN) сертификата. Начиная с Python 3.7, эту проверку выполняет OpenSSL во время рукопожатия, поэтому она проявляется как ошибка проверки сертификата, а не как прежняя ssl.CertificateError.
Почему это происходит
В сертификате нет этого имени хоста
частоСертификат выпущен на другие имена (www., домен без поддомена, старое имя сервиса). Wildcard-имя покрывает ровно одну метку, поэтому *.example.com подходит для api.example.com, но не для example.com или a.b.example.com.
Подключение по IP-адресу, через localhost или по псевдониму контейнера
частоВ URL указан 127.0.0.1, частный IP-адрес, «localhost» или имя сервиса docker-compose, а ни одно из них не встречается в сертификате, выпущенном для публичного DNS-имени.
Сервер вернул сертификат другого сайта
иногдаНа общем хостинге сервер выбирает сертификат по имени из SNI; для имени без собственной конфигурации он отдаёт сертификат по умолчанию.
Как исправить
- 1.
Выведите имена, на которые выпущен сертификат
Сравните записи subjectAltName с хостом, который вы передаёте в 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.
Перевыпустите сертификат со всеми именами в subjectAltName
Добавьте все DNS-имена, а для доступа по IP-адресу — записи типа IP. Имена, указанные только в общем имени (CN), при сопоставлении не используются.
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.
Проверяйте имя из сертификата при подключении по адресу
Если нужно подключиться к конкретному адресу (узлу балансировщика нагрузки, туннелю), оставьте в server_hostname имя из сертификата, а не отключайте проверку имени хоста.
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())
Связанные ошибки
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)