エラーメッセージ
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
別の表記
- ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
Python は C ソースの位置(例:「(_ssl.c:1082)」)を付け加えます。この番号は Python のバージョンによって変わり、診断には意味がありません。urllib はこのエラーを「<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] …>」として、requests と urllib3 は「Max retries exceeded … (Caused by SSLError(…))」としてラップします。
Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20)
このエラーの意味
Python の ssl モジュールが OpenSSL にサーバー証明書の検証を依頼しましたが、OpenSSL は、この SSLContext が読み込んだ CA 証明書の中に、チェーンの最後の証明書の発行者を見つけられませんでした。サーバーの設定が間違っているとは限りません。多くの場合、この Python がブラウザーや OS とは別の(または空の)CA バンドルを参照しているだけです。
発生する理由
証明書が入っていない macOS の python.org 版 Python
よくあるpython.org の macOS 用インストーラーは独自の OpenSSL を同梱しており、この OpenSSL は macOS のキーチェーンを読み込みません。その Python のバージョンで「Install Certificates.command」を実行するまでは CA 証明書がまったくないため、すべての HTTPS リクエストがこのエラーで失敗します。
プライベート CA または TLS を検査するプロキシ
よくある社内サービスや企業のプロキシは、ルートが OS のストアを通じて管理対象のマシンに配布された証明書を使います。Python の OpenSSL は Windows や macOS のストアを参照せず、Linux コンテナーには、追加しない限りそのルートがありません。
サーバーが中間証明書を送信していない
よくあるOpenSSL は不足している中間証明書を取得しません。サーバーがリーフ証明書しか送信しない場合、中間証明書をキャッシュしダウンロードするブラウザーでは問題がないように見えても、Python は失敗します。
CA をシステムのストアに追加したが、ライブラリは certifi を使っている
ときどきrequests、httpx、aiohttp、botocore、pip(従来の証明書処理を使う場合)は、Python パッケージとして配布される certifi のバンドルで検証します。update-ca-certificates や OS のキーチェーンでインストールしたルートは、これらのライブラリからは見えません。
SSL_CERT_FILE または SSL_CERT_DIR が誤った場所を指している
まれこれらの変数は OpenSSL のデフォルトの場所を置き換えます。別のツール用に設定されたまま残っていたパスや、プライベートルートしか含まないバンドルを指定すると、公開サイトへの接続が失敗します(または逆のことが起きます)。
修正方法
- 1.
macOS の python.org ビルド:Install Certificates.command を実行する
このスクリプトは certifi をインストールし、その Python の OpenSSL のバンドルとしてリンクします。別の Python のバージョンをインストールした後は、もう一度実行しなければなりません。
bash # macOS, python.org installer: run once per installed Python version "/Applications/Python 3.14/Install Certificates.command" - 2.
truststore で OS のトラストストアを使う
truststore を使うと、ssl.SSLContext が OS(macOS ではキーチェーン、Windows では証明書ストア、Linux ではシステムのバンドル)を通じて検証するようになるため、IT 部門が配布したルートが Python でも機能します。urllib3、requests、pip にも効果があり、pip は pip 24.2 以降デフォルトで truststore を使います。
python # pip install truststore==0.10.5 — verify with the OS trust store (Keychain, Windows store, Linux bundle) import truststore truststore.inject_into_ssl() # call once at start-up, before creating connections import urllib.request urllib.request.urlopen("https://api.example.com/") - 3.
1 つのクライアントに限って CA を明示的に読み込む
プライベートなサービスと通信するコードでは、ssl.create_default_context() に cafile(または cadata)を渡してください。コンテキストは引き続きホスト名と日付を検証します。
python import ssl, urllib.request ctx = ssl.create_default_context(cafile="/etc/ssl/private-ca/corp-root-ca.pem") urllib.request.urlopen("https://api.example.com/", context=ctx) - 4.
デフォルトのバンドルのパスを確認して設定する
ssl.get_default_verify_paths() を実行すると、OpenSSL が使うファイルとディレクトリがわかります。SSL_CERT_FILE と SSL_CERT_DIR は、プロセス単位でこれらを上書きします。公開ルートと自社の CA の両方を含むバンドルを指定してください。
bash # Where does this Python look by default? python3 -c "import ssl; print(ssl.get_default_verify_paths())" # Point OpenSSL-based clients at a bundle that includes your CA export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt - 5.
サーバー管理者:完全なチェーンを送信する
ハンドシェイクに証明書が 1 つしか含まれておらず、その発行者がパブリックな中間 CA である場合は、完全なチェーンを含むファイルをサーバーに設定してください。
bash # Server side: 1 certificate in the handshake = intermediate missing openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \ | grep -c 'BEGIN CERTIFICATE'
関連するエラー
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unab…Python urllib.requestMax retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1…requests / urllib3 (Python)There was a problem confirming the ssl certificate: [SSL: CERTIFICATE_VERIFY_FA…pip[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certifi…Python ssl module (OpenSSL)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)