エラーメッセージ
[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)が、証明書のサブジェクト代替名に含まれていません。Python 3.7 以降、このチェックはハンドシェイク中に OpenSSL が行うため、以前の ssl.CertificateError ではなく、証明書の検証エラーとして表面化します。
発生する理由
証明書にこのホスト名が記載されていない
よくある証明書は別の名前(www.、apex ドメイン、古いサービス名)を対象にしています。ワイルドカードはちょうど 1 つのラベルにしか一致しないため、*.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 エントリも追加してください。コモンネームにしかない名前は、照合に使われません。
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)