本文へスキップ
ecdsa.com
Python ssl module (OpenSSL)Python の cryptography

エラーメッセージ

[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 で失敗します。開発用サーバー、アプライアンス、インストール直後の環境でよく見られます。対処法は、その証明書を意図的に信頼するか、別の証明書に置き換えることです。

発生する理由

修正方法

  1. 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. 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. 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. 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

関連するエラー

署名エラーデータベースをすべて見る