エラーメッセージ
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate
別の表記
- [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate
Python は C ソースの位置(例:「(_ssl.c:1082)」)を付け加えます。CA のチェーンが信頼されていないルートで終わっていることを意味する「self-signed certificate in certificate chain」と混同しないでください。OpenSSL 1.1.1 では「self signed」と表記されます。
Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (18)
このエラーの意味
サーバーは、このコンテキストの信頼済み CA に含まれていない自己署名証明書(発行者が自分自身)を 1 枚だけ提示しました。参照すべき CA が存在しないため、OpenSSL は深さ 0 で失敗します。開発用サーバー、アプライアンス、インストール直後の環境でよく見られます。対処法は、その証明書を意図的に信頼するか、別の証明書に置き換えることです。
発生する理由
生成された証明書を使う開発用またはテスト用のサーバー
よくあるその場で生成した証明書で起動したローカルサーバー(Flask の ssl_context="adhoc"、生成した鍵を使う uvicorn、docker-compose のスタック)は、どこからも信頼されていない新しい自己署名証明書を提示します。
出荷時の証明書を使っている機器や製品
よくあるNAS、ルーター、Proxmox、Elasticsearch、Jenkins や多くのベンダーのアプライアンスは、管理者が正規の証明書をインストールするまで自己署名証明書を使います。
プロキシが組み込みのデフォルト証明書を返している
ときどきIngress コントローラーやリバースプロキシは、不明なホスト名に対して自己署名のデフォルト証明書で応答します。この場合の本当の問題は、ルーティングか SNI です。
修正方法
- 1.
このクライアントでこの証明書だけを信頼する
サーバー自身の証明書を cafile として読み込んでください。ホスト名と日付のチェックは有効なままで、受け入れられるのはこの証明書そのものだけです。
python import ssl, urllib.request # Trust exactly this certificate (fetched and fingerprint-checked out of band) ctx = ssl.create_default_context(cafile="api.example.com.pem") urllib.request.urlopen("https://api.example.com:8443/", context=ctx) - 2.
証明書を取得してフィンガープリントを確認する
ファイルを信頼する前に、SHA-256 フィンガープリントを、機器のコンソールに表示される値や管理者から伝えられた値と比較してください。
bash openssl s_client -connect api.example.com:8443 -servername api.example.com </dev/null 2>/dev/null \ | openssl x509 -out api.example.com.pem openssl x509 -in api.example.com.pem -noout -fingerprint -sha256 # compare with the server's console - 3.
開発環境:代わりにローカル CA を使う
ローカル CA を使えば、1 つのルートを信頼するだけで、すべての開発用ホストに正しく署名された証明書を発行できます。そのため、どのクライアントにも証明書ごとの例外は必要ありません。
bash # Development: one local CA, many certificates (mkcert installs the CA for you) mkcert -install mkcert api.example.test export SSL_CERT_FILE="$(mkcert -CAROOT)/rootCA.pem" - 4.
一時的な診断:テストで検証を無効にする
check_hostname = False と CERT_NONE は、すべてのチェックを無効にし、攻撃者が提示した証明書も含め、どんな証明書でも受け入れます。信頼だけが問題であることを確認する目的に限って使い、確認後は削除してください。
python # Temporary diagnostic only: proves trust is the sole problem. Never ship this. import ssl ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE