本文へスキップ
ecdsa.com
requests / urllib3 (Python)Python の cryptography

エラーメッセージ

Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

別の表記

  • requests.exceptions.SSLError: HTTPSConnectionPool(host='api.example.com', port=8443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate

ホスト、ポート、URL のパスはリクエストから取られます。最も内側の理由は状況によって異なり(「self-signed certificate in certificate chain」「Hostname mismatch…」「certificate has expired」)、行は「(_ssl.c:1082)')))」のようなソースの位置で終わります。

requests / urllib3 (Python) — requests.exceptions.SSLError wrapping urllib3 MaxRetryError

このエラーの意味

requests が urllib3 に接続を要求したところ、TLS ハンドシェイクで証明書の検証に失敗し、urllib3 は処理を断念しました(requests のデフォルトはリトライなしなので、最初の試行の後で断念します)。役に立つのは括弧の中の部分です。ここでは、requests が使う CA バンドルの中に、OpenSSL が発行者を見つけられませんでした。このバンドルはデフォルトでは certifi が持つ Mozilla のルートのコピーであり、OS のストアではありません。

発生する理由

修正方法

  1. 1.

    verify= で正しいバンドルを渡す

    verify には、CA バンドルファイルのパスか、ハッシュ化された CA ディレクトリのパスを指定できます。ホスト名と日付のチェックは有効なままです。

    python
    import requests
    
    # A CA file (or a directory prepared with c_rehash) for this call
    requests.get("https://api.example.com/", verify="/etc/ssl/certs/ca-certificates.crt")
  2. 2.

    プロセスに REQUESTS_CA_BUNDLE を設定する

    これをシステムのバンドル(自社の CA をそこにインストールした後)に向けると、そのプロセス内のすべての requests の呼び出しが、curl や OS のツールと同じルートを信頼するようになります。

    bash
    # For every requests call in the process (also honoured: CURL_CA_BUNDLE)
    export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt   # Debian/Ubuntu system bundle
    python3 app.py
  3. 3.

    OS のトラストストアを使う

    起動時に truststore.inject_into_ssl() を呼び出すと、requests が OS を通じて検証するようになるため、バンドルを管理しなくても、IT 部門が配布したルートが機能します。

    python
    # pip install truststore==0.10.5 — requests then verifies with the OS trust store
    import truststore
    truststore.inject_into_ssl()
    
    import requests
    requests.get("https://api.example.com/")
  4. 4.

    Session を一度だけ設定する

    アプリケーションでは、公開ルートと会社のルートを含むバンドルを Session.verify に設定し、そのセッションを再利用してください。

    python
    import requests
    
    session = requests.Session()
    session.verify = "/etc/ssl/private-ca/corp-ca-bundle.pem"   # public roots + company root
    session.get("https://api.example.com/")

関連するエラー

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