SSL certificate verify failed
接続先の証明書を信頼できずTLSが中断するこのエラーを、チェーン欠落・期限切れ・ホスト名不一致・時刻ずれと原因別に切り分け、検証を無効化せず正しく直せる。
- 原因の大半は中間CA証明書の欠落によるチェーン不完全、次いで期限切れ・ホスト名不一致・クライアントの時刻ずれ・CAバンドルの古さ。まずどれかを切り分ける。
- 診断は openssl s_client -connect で証明書チェーンと verify return code を確認するのが最短。verify=0 なら OS/言語側の信頼ストアを疑う。
- 検証の無効化は盗聴・なりすましを許すため恒久策にしない。サーバー側でフルチェーンを配信し、クライアント側はCAバンドルと時刻を最新に保つのが正解。
一言でいうと(このエラーが何を意味するか)
SSL certificate verify failed(Python なら CERTIFICATE_VERIFY_FAILED、curl なら SSL certificate problem)は、TLS ハンドシェイク中にクライアントがサーバーから受け取った証明書を信頼できると確認できなかったことを示します。暗号化そのものは動いており、詰まっているのは相手が本物かを確かめる認証の段階です。
クライアントは受け取った証明書(リーフ)から、それを署名した中間CA、さらに上位のルートCAへと証明書チェーンをたどり、末端が自分の信頼ストアにあるルートCAに届くことを検証します。加えて有効期限内であること、CN/SAN が接続先ホスト名と一致すること、失効していないことを確認します。どれか1つでも崩れると検証は失敗し、TLS は中断されます。つまりこのエラーは「通信できない」ではなく「安全だと保証できないので止めた」という防御の作動です。
横にスクロール
よくある原因(複数を具体的に、頻度順)
| 原因 | 何が起きているか | 典型的な追加メッセージ |
|---|---|---|
| 中間CA欠落(チェーン不完全) | サーバーがリーフのみ配信し中間CAを送らず、クライアントが末端まで辿れない | unable to get local issuer certificate |
| 期限切れ | 証明書の notAfter を過ぎた/更新漏れ | certificate has expired |
| ホスト名不一致 | 接続先FQDNが証明書のSANに含まれない | hostname mismatch / no alternative certificate subject name |
| 自己署名 | 公的CAでなく自前で署名した証明書を検証しようとした | self-signed certificate |
| クライアントの時刻ずれ | 端末の時計が大きくずれ、有効期間内でも期限切れ扱いになる | certificate is not yet valid / has expired |
| CAバンドルが古い | クライアント側のルート証明書集(信頼ストア)が古く新ルートを知らない | unable to get issuer certificate |
実務で最も多いのは中間CA欠落です。ブラウザは AIA 拡張から中間CAを補完できる場合があるため「ブラウザでは開けるのに curl やアプリだと失敗する」という食い違いが起きやすく、サーバー設定不備が見逃されがちです。次点が更新漏れによる期限切れで、証明書は放置すれば必ず切れます。ホスト名不一致は www 有無、ワイルドカードの階層(*.example.com は a.b.example.com に一致しない)、IPアドレス直接接続で頻発します。
診断の手順(切り分け方・見るべきログ/コマンド)
まず openssl s_client でサーバーが実際に送っている証明書チェーンを見ます。ここが出発点です。
# -showcerts でチェーン全体、SNI 明示のため -servername を必ず付ける
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
出力末尾の Verify return code を確認します。0 (ok) なら OpenSSL 的には検証成功で、失敗するのは呼び出し側(後述の言語ランタイム)の信頼ストアが原因です。20 (unable to get local issuer certificate) は中間CA欠落かローカルのルート不足、10 は期限切れ、18 (self-signed certificate) はリーフが自己署名、21 (unable to verify the first certificate) は中間CA欠落などでリーフの署名を検証できない状態を意味します。
次にチェーンの中身と有効期限を個別に確認します。
# サーバーが提示した各証明書の subject / issuer / 期限を一覧化
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName
各証明書の issuer が次の証明書の subject と鎖のように繋がり、最後がルートCAに届いていればチェーンは完全です。途中で途切れていれば中間CA欠落が確定します。SAN 一覧に接続先 FQDN が無ければホスト名不一致です。
ブラウザで正常・CLI で失敗するときは、まず中間CA欠落を疑ってください。ブラウザは過去にキャッシュした中間CAや AIA からの取得で欠落を補いますが、curl・Java・Python・Go はサーバーが送ったチェーンだけで判断します。「ブラウザで見えるから正しい」は誤りで、CLI の結果がサーバー設定の実態を映します。
言語ランタイム由来かの切り分けも重要です。OpenSSL では通るのに Python だけ失敗する場合、多くは信頼ストアの参照先が違います。
# Python が実際に参照している CA バンドルの場所を確認
python -c "import ssl; print(ssl.get_default_verify_paths())"
時刻ずれは date で端末時計を確認します。証明書の notBefore より前、あるいは notAfter より後に時計がずれていれば、有効な証明書でも「まだ有効でない/期限切れ」と判定されます。コンテナや組み込み機器、長期停止していた端末で起こりがちです。
解決と予防(対処と再発防止)
原因別の対処は次の通りです。サーバー側の不備はサーバーで直すのが原則で、全クライアントに手を入れる回避策より根本的です。
| 原因 | 対処 | 再発防止 |
|---|---|---|
| 中間CA欠落 | サーバーにリーフ+中間CAを連結したフルチェーンを配置し配信 | CIで s_client によるチェーン検査を自動化する |
| 期限切れ | 証明書を再発行・更新し配置し直す | ACME(Let's Encrypt等)で自動更新、期限を監視・アラート |
| ホスト名不一致 | 接続先FQDNをSANに含む証明書を発行、または正しいホスト名で接続 | 発行時にSAN一覧を必ず確認する |
| 自己署名/プライベートCA | そのCA証明書をクライアントの信頼ストアに正規登録する | 社内CAの配布を構成管理で徹底 |
| 時刻ずれ | NTPで時刻同期する | 全ノードでNTP/chronyを常時有効化 |
| CAバンドルが古い | OSと ca-certificates、言語のcertifi等を更新 | 定期的なパッケージ更新に含める |
中間CA欠落の修正はサーバーへのフルチェーン配信が要点です。多くの実装は、リーフ証明書の下に発行元の中間CAを連結した1ファイル(末尾がルート直下の中間CAまで)を読み込ませればチェーンが完成します。
Python アプリで信頼ストアが古い・見つからない場合は、標準的な CA バンドルを明示するのが確実です。
import certifi, ssl, urllib.request
ctx = ssl.create_default_context(cafile=certifi.where())
urllib.request.urlopen("https://example.com", context=ctx)
verify=False、curl -k、NODE_TLS_REJECT_UNAUTHORIZED=0 などで検証を切ると、確かにエラーは消えます。しかしそれは暗号化を保ったまま認証を放棄する行為です。中間者攻撃者が偽の証明書を提示しても素通しになり、通信内容の盗聴・改ざん・なりすましを許します。エラーは攻撃を止めていた防御であって、消すべきは失敗の原因(チェーンや期限)です。どうしても検証をずらす必要があるなら、無効化ではなく該当CA証明書を信頼ストアへ限定的に追加してください。
再発防止の軸は自動化です。証明書は必ず切れるので、ACME による自動更新と期限監視をセットで導入し、デプロイ時に openssl s_client でチェーンが末端まで繋がるかを検査する仕組みを CI に組み込みます。TLS と PKI の基礎はセキュリティ、ハンドシェイクとSNIの仕組みはネットワーク、HTTPS 配信側の設定はWebを参照してください。
- 検証失敗は暗号化ではなく「認証」の失敗。通信不能ではなく防御の作動である
- 最頻の原因は中間CA欠落=チェーン不完全。ブラウザは補完できるがCLI/ランタイムは補完しない
- 診断の第一手は openssl s_client -connect ... -showcerts と Verify return code の確認
- 検証無効化は中間者攻撃を素通しにするため恒久策にしてはならない
エラー辞典の記事ガイド
SSL certificate verify failedを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
TLS
比較で見る軸
難易度: advanced / カテゴリ: エラー辞典 / タグ数: 6
導入後に効く点
診断は openssl s_client -connect で証明書チェーンと verify return code を確認するのが最短。verify=0 なら OS/言語側の信頼ストアを疑う。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- エラー辞典
- タグ数
- 6
判断チェックリスト
- 自社の用途が「TLS / SSL」に近いか確認する。
- 強みである「原因の大半は中間CA証明書の欠落によるチェーン不完全、次いで期限切れ・ホスト名不一致・クライアントの時刻ずれ・CAバンドルの古さ。まずどれかを切り分ける。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。