Python requests で HTTPS リクエスト時の certificate verify failed: self signed certificate in certificate chain エラーに対応する
Python の requests
パッケージで https インターネットアクセスする際に、TLS インスペクションが入っている環境で発生する certificate verify failed: self signed certificate in certificate chain
エラーに対応する方法を紹介します。
やりたいこと
Python で http リクエストを送る際に requests
パッケージを使用することはよくあると思います。
ところが企業の環境等で TLS インスペクションが入っている環境において requests
パッケージでインターネットアクセスする際に
certificate verify failed: self signed certificate in certificate chain
というエラーが発生することがあります。このエラーの対応方法を紹介します。
環境
- OS
- Microsoft Windows 10 22H2
- Python
- 3.11.2
- requests
- 2.32.3
事象
例えば TLS インスペクションがある環境で以下のようなコードで Yes か No を返してくれる YESNO API を叩いてみます。
import requests
res = requests.get("https://yesno.wtf/api")
print(res.content)
すると以下のようなエラーが発生します。
requests.exceptions.SSLError: HTTPSConnectionPool(host='xxx.com', port=443): Max retries exceeded with url: /api (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:992)')))
このエラーは TLS インスペクションが入っていることにより証明書検証でエラーとなっていることがほとんどです。 TLS インスペクションの簡単な説明と、後ほど使用する証明書の特定、取得方法を こちらの記事 で紹介していますので必要であればご参照ください。
対応方法
証明書検証をオフにする (非推奨)
requests
では requests.request()
メソッドのキーワード引数 verify
に False
を渡すことで証明書検証をオフにすることができます。ただしあらゆる HTTPS における証明書検証がなされなくなるので、この方法はおすすめしません。
なお、キーワード引数 verify
は requests.request()
のラップメソッドである requests.get()
や requests.post()
などでも同様に使用できます。
verify – (optional) Either a boolean, in which case it controls whether we verify the server’s TLS certificate, or a string, in which case it must be a path to a CA bundle to use. Defaults to True. Developer Interface - Requests 2.32.3 documentation
verify=False
として実行すると、
import requests
res = requests.get("https://yesno.wtf/api", verify=False)
print(res.content)
以下のワーニングが表示されますが、
InsecureRequestWarning: Unverified HTTPS request is being made to host 'xxx.xxx'. Adding certificate verification is strongly advised.
API のレスポンスは受け取れていることがわかります。
{
"answer": "yes",
"forced": false,
"image": "https://yesno.wtf/assets/yes/0-c44a7789d54cbdcad867fb7845ff03ae.gif"
}
自己署名証明書を指定する
本質的にはプロキシの自己署名証明書を追加することがあるべき姿です。
requests.request()
メソッドのキーワード引数 verify
の公式ドキュメントの記載を再掲しますが、 verify
は Boolean 値以外に証明書パスを指定できます。繰り返しになりますが、追加する証明書の特定、取得方法を こちらの記事 で紹介していますので必要であればご参照ください。
verify – (optional) Either a boolean, in which case it controls whether we verify the server’s TLS certificate, or a string, in which case it must be a path to a CA bundle to use. Defaults to True. Developer Interface - Requests 2.32.3 documentation
キーワード引数 verify
に証明書パスを指定して実行するとワーニングも出ずに API のレスポンスが取得出来ると思います。
import requests
res = requests.get("https://yesno.wtf/api", verify="C:/path/to/cert.pem")
print(res.content)
環境変数で自己署名証明書を指定する
requests
では環境変数 REQUESTS_CA_BUNDLE
で証明書を指定することもできます。サードパーティーのパッケージの中には http リクエストを行うために requests
を組み込んでいるものが多くあります。例えば kagglehub
パッケージの model_download()
メソッドはその一つですが、model_download()
の引数に verify
のようなものは用意されていません。そのような場合、環境変数で証明書をしていすることで外から証明書パスを指定することができます。
This list of trusted CAs can also be specified through the
REQUESTS_CA_BUNDLE
environment variable. Advanced Usage - Requests 2.32.3 documentation
環境変数 REQUESTS_CA_BUNDLE
に証明書パスを指定して実行すると問題なく API レスポンスが取得できます。
import os
import requests
os.environ["REQUESTS_CA_BUNDLE"] = "C:/path/to/cert.pem"
res = requests.get("https://yesno.wtf/api")
print(res.content)
ここでは Python で環境変数を設定していますが、PowerShell やコマンドプロンプトで設定することも可能です。
あとがき
TLS インスペクションが入った環境化における Python の requests
パッケージで HTTPS リクエスト時に発生する certificate verify failed: self signed certificate in certificate chain
エラーの対応方法について紹介しました。まだまだ OS の証明書ストアを見てくれないものが多いので個別に対応する必要があり面倒ですね。