代表的な面接トピック

TLS 証明書の検証はどのように機能し、どのように障害をデバッグするか?

一般難しい
Offer.cc 編集チーム公開日 更新日

質問

api.example.com が TLS 証明書をローテーションした後、ブラウザと curl は成功しますが、カスタムトラストストアを使用する Java 21 ワーカーが SunCertPathBuilderException で失敗します。なぜクライアント間で結果が異なるのか、そして検証を無効化せずにどのように障害を診断・修復しますか?

質問と適用されるコンテキスト

api.example.com が TLS 証明書をローテーションした後、ブラウザと通常の curl https://api.example.com リクエストは成功します。カスタムトラストストアを使用する Java 21 ワーカーは SunCertPathBuilderException: unable to find valid certification path で失敗します。一方、curl https://203.0.113.10 は同じロードバランサーに到達しますが、 ID 検証に失敗します。

TLS クライアントが証明書パスをどのように構築および検証するか、目的のサービス ID をどのように 照合するか、これらのクライアントがなぜ異なる結果に至るのか、そして証明書やホスト名の検証を 無効にすることなく、どのようにインシデントを診断および修復するかを説明してください。

以下の明示的な前提条件を使用してください。api.example.com および 203.0.113.10 は架空のものです。 エンドポイントは通常の公的に信頼されたサーバー証明書を使用しています。Java ワーカーの カスタムトラストストアはブラウザの信頼設定から独立しています。ロードバランサーは 単一の IP アドレスで複数の TLS 名をホストできます。課題は証明書認証と運用上の診断です。 暗号スイートのネゴシエーション、TLS 1.3 の鍵スケジュール、および 0-RTT は主なスコープ外です。

この質問は、一般的なソフトウェアエンジニアリング、ネットワーキング、SRE、プラットフォーム、セキュリティ、 バックエンド、およびクライアントの面接に適しています。有益な回答をするには、「リーフ、中間、ルート」を 単に暗唱するのではなく、PKI のルールを観察可能なクライアントの動作に関連付ける必要があります。

面接官が評価するポイント

第一に、候補者が往々にして 1 つにまとめられがちな 4 つの決定を分離できるかどうか。

  1. パスの構築(Path construction): リーフから 0 個以上の中間証明書を経由して、

ローカルで設定されたトラストアンカーに至る候補シーケンスを 1 つ見つける。

  1. パスの検証(Path validation): 候補パスが署名、有効期限、CA 制約、キー使用法、

名前制約、クリティカル拡張、アルゴリズムとポリシー要件、および意図された TLS サーバーの用途を 満たしていることを検証する。

  1. サービス ID の照合(Service-identity matching): 設定された参照ホスト名または IP アドレスを、

対応する subjectAltName 識別子と照合する。

  1. ハンドシェイクの証明(Handshake proof): ピアがリーフ証明書の秘密鍵を所持していることを証明し、

それをこのハンドシェイクにバインドするために CertificateVerify を検証する。

第二に、候補者が普遍的な原因をでっち上げることなく、クライアント間の不一致を説明できるかどうか。 ブラウザ、OS ツール、コンテナ、JVM、モバイルアプリ、企業プロキシは、それぞれ異なる トラストアンカー、キャッシュされた中間証明書または検出可能な中間証明書、クロック、 アルゴリズム、失効ポリシー、参照 ID を使用する可能性があります。

第三に、候補者が管理された調査を実行できるかどうか。優れた回答では、正しい SNI で配信された 正確な証明書チェーンをキャプチャし、失敗しているクライアントの信頼マテリアルで検証を再現し、 IP を固定しながらホスト名を維持し、成功したパスと失敗したパスを比較し、すべてのロードバランサーインスタンスを チェックします。

最後に、候補者が信頼関係を安全に修復できるかどうか。任意のリーフをインポートしたり、すべての証明書を 受け入れたり、ホスト名チェックをスキップしたり、curl -k を使用したりすることは、失敗したセキュリティ制御を 隠すだけにすぎません。修復では、意図したチェーンまたは信頼ポリシーを復元し、一時的な信頼の変更に対する ロールバックおよび有効期限プランを含める必要があります。

回答前に確認すべき質問

  • 各クライアントが使用している正確な URL と参照 ID は何か? IP リテラルへの接続は

IP ID を要求します。URL https://api.example.com を維持しながら同じ IP に接続すると、 DNS ID api.example.com が要求されます。

  • 実行時にアクティブなトラストストアはどれか? 開発者のノート PC のデフォルトストアを

確認するのではなく、実際の JVM オプション、コンテナイメージ、ユーザー、プロセス環境、 およびマウントされたトラストストアを確認します。

  • 失敗したリクエストはどのようなチェーンを受信したか? SNI、ロードバランサーリスナー、

リージョン、プロキシ、IPv4 と IPv6 の違い、デプロイのスキューによって、提示される証明書が変わる可能性があります。

  • すべてのクライアントが同時に失敗したか? ローカル時刻、証明書の有効期間、CA バンドルの

バージョン、アルゴリズムポリシー、および単一のバックエンドまたはエッジインスタンスのみが 新しいチェーンを提供していないかどうかを確認します。

  • TLS はインターセプトされているか? 企業のプロキシが公開リーフを、ブラウザは信頼しているが

カスタム JVM トラストストアは信頼していないエンタープライズルートによって発行された証明書に 置き換えている可能性があります。

  • エラーは何を特定しているか? パス構築エラー、期限切れ証明書、ホスト名の不一致、

サポートされていないアルゴリズム、失効エラー、TLS ネゴシエーションエラーは異なる分岐です。 完全な例外と検証トレースを保持してください。

30秒の回答フレームワーク

「私はチェックをパス構築、パス検証、サービス ID 照合、秘密鍵の証明に分割します。 サーバーは通常、リーフと必要な中間証明書を送信し、クライアントはローカルですでに信頼されている トラストアンカーへのパスを構築します。次に、署名、有効期限、CA および鍵の制約、クリティカル拡張、 アルゴリズム、サーバーの用途を検証します。それとは別に、設定された DNS 名または IP アドレスを 同じタイプの SAN エントリと照合します。SNI はサーバーが証明書を選択するのに役立ちますが、 信頼を確立したりホスト名を証明したりするわけではありません。

カスタム JVM トラストストア、中間証明書の検出、プロキシのルート、クロック、ポリシーが異なる可能性があるため、 ブラウザの成功は Java パスが有効であることを証明しません。正しい SNI でチェーンをキャプチャし、 ワーカーの正確なトラストストアに対して再生し、構築されたパスを比較し、curl --resolve を使用して IP をターゲットにしながら DNS ID を一定に保ちます。検証を無効にすることは絶対にせず、 配信される中間証明書または意図的に管理されたトラストアンカーを修正し、すべてのエッジと実際の実装ワーカーを再テストします。」

ステップバイステップの詳細解説

1. 3つの独立した入力を確立する。 検証器には、ターゲットのリーフ証明書、パスの構築に 役立つ可能性のある信頼されていない中間証明書のセット、および 1 つ以上のローカルトラストアンカーが 必要です。ここでの「信頼されていない」とは悪意があるという意味ではなく、中間証明書はパス構築の マテリアルであり、サーバーが送信したからといってトラストアンカーになるわけではないことを意味します。

HTTPS サーバーは通常、リーフ証明書に続いてクライアントが必要とする中間証明書を送信します。 通常、ルートを送信する必要はありません。検証器の信頼の判断は、ローカルポリシーによって設定された ルートまたは他のアンカーで終了します。ルートを送信しても、未知のルートが信頼されるようになるわけではありません。

2. 検証の前に候補パスを構築する。 リーフは発行者を指定し、中間証明書は別の発行者を 指定でき、クロス署名された CA は複数の可能なルートを作成できます。クライアントは、ハンドシェイク、 キャッシュ、または実装固有の検出メカニズムから中間証明書を取得できます。RFC 5280 はパス検証を 定義していますが、候補シーケンスを取得する手順は意図的にスコープ外としています。したがって、 標準に準拠した 2 つのクライアントが異なる構築入力を持ち、異なるパスを選択する可能性があります。

SunCertPathBuilderException は、Java パスビルダーが利用可能な証明書とポリシーで受け入れ可能なパスを 見つけられなかったことを意味します。考えられる原因には、中間証明書の欠落、カスタムストア内の トラストアンカーの不在、使用不可能な代替パス、ポリシーの制約などがあります。例外ログ単体では、 そのうちどれが発生したかを証明できません。

3. 選択されたパスを検証する。 関連する各リンクについて、クライアントは発行者の公開鍵で 証明書の署名を検証し、制約を処理します。重要なチェック項目は以下のとおりです。

  • 現在の時刻が証明書の有効期間内にあること。
  • すべての発行証明書が basicConstraints の下で CA として機能することを許可されており、

パス長の制限が尊重されていること。

  • CA のキー使用法で証明書の署名が許可されており、リーフが該当するキー使用法および拡張キー使用法ポリシーの下で

意図された TLS サーバーの目的に適していること。

  • 名前制約、証明書ポリシー、および認識されたクリティカル拡張が正しく処理されていること。
  • アルゴリズムと鍵強度がクライアントの現在のセキュリティポリシーを満たしていること。
  • パスがローカルポリシーによって選択されたトラストアンカーで終了していること。

失効もポリシーの重要な側面です。クライアントによって、CRL、OCSP レスポンス、ステープルされたステータス、 ネットワークエラー、ソフトフェイルとハードフェイルの条件の取得方法および処理方法が異なります。 「RFC 5280 パス検証に合格した」からといって、すべてのクライアントが同一のオンライン失効チェックを実行したと 想定してはいけません。

4. サービス ID を個別に照合する。 参照 ID は、逆引き DNS、証明書自体、または攻撃者が提供する 任意の名前からではなく、設定された HTTPS URL などの信頼できる入力から取得されます。RFC 9525 では、 最新のサービス ID は subjectAltName で表されます。クライアントはこの目的のためにサブジェクトの Common Name に フォールバックしてはなりません。

DNS 名と IP アドレスでは使用する SAN タイプが異なります。https://api.example.com には 一致する DNS-ID が必要です。https://203.0.113.10 には iPAddress SAN 内の正確な IP アドレスが必要です。 api.example.com を含む DNS SAN では不十分です。サポートされている場合、ワイルドカードは 左端のラベル全体である必要があり、正確に 1 つのラベルに一致します。*.example.comapi.example.com に一致できますが、v2.api.example.comexample.com には一致しません。

SNI と検証には異なる役割があります。SNI はマルチテナントサーバーに対して提示すべき証明書を 指示します。参照 ID はクライアントに対して、その証明書がカバーしなければならない名前を指示します。 リクエストが正しい SNI を送信してもホスト名チェックに失敗する場合や、SNI を省略して デフォルトの証明書を受信し、別の理由で失敗する場合があります。

5. リーフ秘密鍵の所持を検証する。 有効なパスと一致する SAN により、ID が公開鍵にバインドされます。 証明書で認証された TLS 1.3 ハンドシェイク中、CertificateVerify は対応する秘密鍵で ハンドシェイクのトランスクリプトに署名します。これを検証することで、ピアがこのネゴシエーションにおいて その秘密鍵を制御していることが証明されます。これは、パスの構築やホスト名の照合とは異なります。

6. 3つの観察結果が両立する理由を説明する。 これらの観察結果は矛盾していません。

観察結果確定すること確定しないこと
ブラウザが成功するそのブラウザの環境下で許容可能なパスと ID が見つかったカスタム JVM が同じルート、中間証明書、プロキシ、クロック、またはポリシーを持っていること
curl https://api.example.com が成功するcurl のアクティブなバックエンドと CA ソースがそのエンドポイントを受け入れたcurl と Java が同一の検証入力を使用していること
curl https://203.0.113.10 が ID 検証に失敗する証明書に一致する IP-ID がないか、別の証明書が選択されたDNS 名アクセスも失敗するはずであること
Java パスビルダーが失敗するワーカーの入力とポリシーの下で許容可能なパスが構築されなかったリーフが普遍的に無効であること、またはホスト名照合に到達したこと

7. エンドポイントとパスを個別に再現する。 正確な DNS 名と SNI から始めます。 次のフラグは OpenSSL 3.6 を対象としています。macOS システムの LibreSSL や古いパッケージでは 異なるオプションが公開されているため、最初に openssl version を確認してください。-showcerts は サーバーが送信した内容を表示します。-verify_return_error は検証エラーで停止します。 -verify_hostname は意図した DNS ID をチェックします。

bash
openssl s_client \
  -connect api.example.com:443 \
  -servername api.example.com \
  -showcerts \
  -verify_return_error \
  -verify_hostname api.example.com \
  </dev/null

リーフ証明書と中間証明書を個別の PEM ファイルとして保存し、承認されたエクスポートを通じて 失敗している環境から正確な信頼されたルートを取得し、パス検証を明示的に再生します。

bash
openssl verify \
  -CAfile worker-roots.pem \
  -untrusted served-intermediates.pem \
  -purpose sslserver \
  -verify_hostname api.example.com \
  leaf.pem

この実験により、トラストアンカーと中間証明書が区別されます。JVM のすべてのアルゴリズム、 失効、またはプロバイダのルールを完全にエミュレートするわけではないため、決定的な再現は トラストマネージャーのデバッグ出力と同じトラストストアを使用して、同じ Java イメージ内で実行します。 ログを共有する前に、内部名と証明書マテリアルを秘匿化してください。

HTTPS 参照名を変更せずに特定のロードバランサーのアドレスをターゲットにするには、 URL を保持して名前解決をオーバーライドします。

bash
curl --resolve api.example.com:443:203.0.113.10 \
  https://api.example.com/health

アドバタイズされているすべての IPv4 および IPv6 アドレス、リージョン、エッジインスタンスに対して これを繰り返します。https://203.0.113.10 を直接リクエストすることは別の ID テストであり、 api.example.com の証明書が間違っていることの証拠として使用すべきではありません。

8. 失敗したレイヤーを修復し、ロールバックパスを証明する。 サーバーに必要な中間証明書が 不足している場合は、すべての TLS ターミネーターに正しいリーフと中間のバンドルをデプロイし、 提供されるチェーンを確認します。組織が意図的にプライベート CA またはプロキシ CA を使用している場合は、 所有権、スコープ、フィンガープリント、有効期限、およびロールバックを記録した上で、管理された トラストストアプロセスを通じて承認されたルートを配布します。間違った SAN が発行された場合は、 証明書を再発行します。クロック、古いインスタンス、またはアルゴリズムポリシーが異なる場合は、 それらの状態を直接修正します。

エンドポイントのリーフを永続的なルートとしてインポートしたり、ブラウザのキャッシュから出所不明の 証明書をコピーしたり、エンドポイント識別を無効にしたり、寛容なトラストマネージャーをインストールしたり、 curl -k をリリースしたりしないでください。修復後は、実際のワーカー、ブラウザ、curl、 すべてのエッジアドレス、以前の証明書の削除、期限切れ前のモニタリング、および意図的に間違えたホスト名や 信頼されていないテストチェーンに対する拒否動作を確認してください。

質の高い模範回答

「ブラウザが動作しているからといって、Java が間違っているとは判断しません。各検証器は、 独自のターゲット証明書、中間証明書セット、トラストアンカー、参照 ID、時刻、ポリシーに基づいて 決定を下します。

私は 4 つのチェックを切り分けます。まず、パス構築により、リーフから中間証明書を経由して ローカルで信頼されたアンカーまでのシーケンスを見つけます。サーバーによって送信された証明書は単なる 構築の入力であり、信頼を生み出すものではありません。次に、パス検証により、署名、有効期間、CA および 鍵の制約、クリティカル拡張、アルゴリズム、ポリシー、およびサーバー認証の目的を確認します。 第 3 に、エンドポイント識別により、設定された名前を SAN と比較します。DNS URL には DNS-ID が必要ですが、 IP リテラル URL には IP-ID が必要です。SNI は仮想ホストの証明書を選択するだけです。第 4 に、TLS は CertificateVerify を検証して、ピアがこのハンドシェイクのリーフ秘密鍵を保持していることを証明します。

ブラウザには、異なるルートストア、キャッシュまたは検出された中間証明書、またはエンタープライズプロキシの ルートが存在する可能性があります。Java 21 ワーカーのカスタムトラストストアには、必要なアンカーまたは 構築入力が不足している可能性があり、その例外ログは単に受け入れ可能なパスが構築されなかったことのみを示しています。 証明書が DNS 名をカバーしているものの IP SAN を持たない場合、IP による curl の失敗は想定どおりの動作です。

まず、openssl s_client、正しい SNI、-verify_return_error-verify_hostname を使用して正確なチェーンを キャプチャします。SAN、発行者、有効期間、基本制約、キー使用法、EKU、アルゴリズム、フィンガープリントを インベントリ化します。次に、保存されたリーフと提供された中間証明書をワーカーのルートの承認済み エクスポートに対して検証し、実際のランタイムフラグを使用して Java コンテナ内で再現します。curl --resolve を 使用すると、api.example.com を URL ID と SNI の両方として維持しながら、各ロードバランサー IP をテストできます。

中間証明書が不足している場合は、すべての TLS ターミネーターでチェーンバンドルを修正します。 承認されたプライベートまたはプロキシのルートが存在しない場合は、リーフを直接信頼するのではなく、 管理されたトラストストアワークフローを介してそのルートを配布します。SAN、クロック、またはポリシーが 間違っている場合は、そのレイヤーを正確に修復します。決してすべてを信頼する設定(trust-all)にしたり、 ホスト名検証を無効にしたり、-k を修正策として受け入れたりしません。実際の実装ワーカーがすべてのエッジで 成功し、かつネガティブテストによって間違ったホスト名や信頼されていないチェーンが引き続き拒否されることを 確認して初めて、インシデントをクローズします。」

よくある間違い

  • チェーンの構築と検証を単一の操作として扱う → クライアントは検証前に異なる候補パスを

発見する可能性があります → ターゲット、中間証明書セット、トラストアンカー、ポリシーを個別に挙げてください。

  • サーバーが送信したからという理由でルートを信頼する → トラストアンカーはローカルポリシーから

取得されます → サーバーから提供された証明書は、信頼されていない構築用入力として扱ってください。

  • 署名をチェックするが CA 制約を省略する → 有効な署名があることだけで、発行者が証明書に署名する権限を

持っていることにはなりません → 基本制約、パス長、キー使用法、クリティカル拡張、用途を確認してください。

  • 証明書内の名前を期待される ID として使用する → これにより、提示されたデータ自身に証明すべき内容を

選択させることになります → 設定された URL から参照 ID を導出してください。

  • SNI がホスト名検証を実行すると仮定する → SNI は仮想ホストを選択するだけです →

独立したクライアントチェックとして SAN 照合を実行してください。

  • DNS SAN が IP URL を検証することを期待する → DNS-ID と IP-ID は異なるタイプです →

DNS ID を保持しながらルーティングを固定することが目的の場合は、--resolve を使用してください。

  • 1つの例外から中間証明書の欠落のみを原因と決めつける → トラストストア、ポリシー、時刻、

プロキシ、デプロイスキューによって同様の症状が発生する可能性があります → **実際のチェーンをキャプチャし、 正確なランタイム入力で再現してください。**

  • -k や trust-all で修復する → これにより、認証されたチャネルが認証されていない

チャネルに変わってしまいます → チェーン、ID、管理されたルート、クロック、またはポリシーを修正してください。

フォローアップの質問と回答

フォローアップ 1: サーバーはルート証明書を送信すべきですか?

通常は送信すべきではありません。サーバーは、クライアントがすでに信頼しているルートに到達するために 必要なリーフと中間証明書を送信する必要があります。サーバーによって送信されたルートは、それを信頼している クライアントにとっては冗長であり、信頼していないクライアントにとっては無力です。また、ハンドシェイクのバイト数も浪費します。

フォローアップ 2: なぜブラウザは中間証明書の欠落から回復できるのに、別のクライアントは失敗するのですか?

実装によって、中間証明書のキャッシュや検出の動作が異なる場合があります。あるブラウザはすでに 中間証明書を保持しているか、実装固有のメカニズムを通じて取得する可能性がありますが、 隔離されたワーカーにはハンドシェイクとカスタムストアしかありません。このため、サーバーはクライアントの 回復機能に頼るのではなく、必要な中間証明書を配信する必要があります。すべてのブラウザが同じように動作すると 仮定するのではなく、実際のパスを確認してください。

フォローアップ 3: ホスト名が一致していれば、自己署名証明書は安全ですか?

いいえ。ID の照合とパスの信頼性は独立しています。証明書に正しい DNS SAN が含まれていても、 ローカルで信頼されたアンカーへのパスが存在しない場合があります。プライベートなデプロイメントでは、 管理されたプロビジョニングを通じて自己署名ルートを信頼できますが、単に名前が一致しているだけでは その信頼は確立されません。

フォローアップ 4: 面接の回答で失効についてどのように議論すべきですか?

ポリシーの境界を述べてください。CRL、OCSP、ステープリング、キャッシュされたステータス、ネットワークアクセス、 ソフトフェイルまたはハードフェイルのルールは、クライアントによって異なります。実際の検証器が何をチェックし、 ステータスが利用できない場合にどのように動作するかを判断してください。すべてのクライアントが同一の オンラインチェックを実行すると主張してはならず、障害を解消するために必須のハードフェイルポリシーを 密かに緩和してはなりません。

フォローアップ 5: このインシデントをクローズするにはどのような証拠が必要ですか?

エッジごとに提供されたリーフおよび中間証明書のフィンガープリント、ワーカーの正確なルートの下で構築されたパス、 成功した DNS 名および秘密鍵の証明、実際のワーカーリクエストの成功を記録します。間違った DNS 名、 IP-ID のない IP リテラル、信頼されていないチェーンに対するネガティブテストを追加します。 バイパスが残っていないこと、すべてのロードバランサーインスタンスが意図したバンドルを提供していること、 モニタリングが有効期限とローテーションをカバーしていること、一時的なルートや診断アーティファクトに 所有者と削除日が設定されていることを確認します。

公開情報ソース

関連する質問