エラーメッセージ
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain
別の表記
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain
OpenSSL 3 は「self-signed」、OpenSSL 1.1.1 は「self signed」と出力し、Python は「(_ssl.c:1082)」のようなソースの位置を付け加えます。urllib 経由では「<urlopen error …>」の中に、requests 経由では「Max retries exceeded … (Caused by SSLError(…))」の中に現れます。
Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN (19)
このエラーの意味
サーバーが提示したチェーンは、この Python プロセスが信頼する CA に含まれていない自己署名のルート証明書で終わっています。チェーン自体は完全で、ルートが不明なだけです。pypi.org や api.github.com のような公開サイトでこのエラーが起きる場合、そのルートは、ネットワーク上またはマシン上にある TLS を検査するプロキシやウイルス対策ソフトのものです。
発生する理由
企業のプロキシやウイルス対策ソフトが HTTPS を傍受している
よくある傍受用のルートは IT 部門によって Windows や macOS のストアにインストールされているため、ブラウザーは動作します。Python の OpenSSL や、certifi のバンドルを使うライブラリは、これらのストアを読み込みません。
requests、pip、httpx が certifi で検証している
よくあるcertifi に含まれているのは Mozilla の公開ルートだけです。会社のルートをシステムのストアに追加しても certifi のファイルは変わらず、pip で certifi をアップグレードすると手動で加えた編集は上書きされます。
社内サービスが独自のプライベートルートを送信している
ときどき会社の CA で署名され、チェーンにルートを含めて送信する社内 API であっても、クライアント側にそのルートを設定する必要があります。
独自のバンドルを持つ conda、pyenv、仮想環境
ときどきconda は独自の OpenSSL と CA バンドルを同梱しているため、システムの Python に適用した修正は conda には反映されません。
修正方法
- 1.
誰のルートかを特定する
公開ホストのチェーンを出力してください。会社名やセキュリティ製品の名前が付いたルートであれば、それが信頼すべきルートです。信頼できないネットワークで見覚えのない名前が表示された場合、それは信頼する理由ではなく、作業を中止する理由です。
bash openssl s_client -connect pypi.org:443 -servername pypi.org -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:' # A top certificate named after your company or a security vendor = interception proxy - 2.
OS のトラストストアで検証する
truststore.inject_into_ssl() を呼び出すと、Python の TLS が OS のトラストストアを使うようになります。このストアには、組織が配布したルートがすでに含まれています。管理下にある Windows や macOS のマシンでは、これが最もすっきりした解決策です。
python # pip install truststore==0.10.5 import truststore truststore.inject_into_ssl() # the proxy root deployed in the OS store is now trusted import requests requests.get("https://api.example.com/") - 3.
公開ルートと自社のルートを含むバンドルを作成する
REQUESTS_CA_BUNDLE(requests)と SSL_CERT_FILE(ssl、urllib、httpx)はデフォルトのバンドルを置き換えます。そのため、会社のルートだけを指定するのではなく、certifi のルートと会社のルートを連結したファイルを指定してください。
bash # Keep the public roots and add the company/proxy root cat "$(python3 -m certifi)" corp-root-ca.pem > ~/.config/corp-ca-bundle.pem export REQUESTS_CA_BUNDLE=~/.config/corp-ca-bundle.pem # requests export SSL_CERT_FILE=~/.config/corp-ca-bundle.pem # ssl, urllib, httpx - 4.
conda:conda 専用の設定を行う
conda は .condarc から ssl_verify を読み込みます。これに連結したバンドルを設定すれば、プロキシの背後でも conda install と conda update が動作します。
bash # conda keeps its own OpenSSL and CA bundle conda config --set ssl_verify /path/to/corp-ca-bundle.pem
関連するエラー
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local…Python ssl module (OpenSSL)[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certifi…Python ssl module (OpenSSL)There was a problem confirming the ssl certificate: [SSL: CERTIFICATE_VERIFY_FA…pipcurl: (60) SSL certificate problem: self-signed certificate in certificate chaincurl (OpenSSL backend)