要約

  • RFC 5280 のポリシーツリーは、複数のマッピングが同じ状態へ到達するとその状態を重複して持ち、深さごとに子孫を再複製するため、最悪時に指数的に拡大する。
  • RFC 9618 の有向非巡回グラフは同一深度・同一 OID のノードを共有する。証明書パスの有効性と最終ポリシー集合を変えず、状態量を入力のポリシーとマッピングに対して線形に抑える。
  • 移行の証拠には、複雑な正当ケースでの意味的等価性と、敵対的入力に対するメモリ・CPU・待ち行列の上限の両方が必要である。

インシデントの最初の兆候は、誤った証明書判定ではなかった。クライアント証明書を検証するワーカーが戻らず、正当な接続がその後ろで待ち始めたことだった。提出されたチェーンは短く、署名を偽造してもいない。それでも、ポリシーマッピングの組合せが内部状態を膨張させた。

RFC 9618が扱うのは、この「答えに至るまでの権限」である。未認証の相手が検証器へ入力を渡せるからといって、無制限の計算を命じる権限まで与える必要はない。

パス構築とポリシー検証を分ける

X.509 の証明書ポリシー拡張は、OID で表すポリシーと任意の修飾情報を含む。CA 証明書は適用可能なポリシーを絞り、発行者側の OID を主体側の別 OID に写像できる。利用側は受け入れ可能な初期ポリシー集合を与える。

RFC 5280の検証処理は、証明書、制約、マッピングを通過して残るポリシーを求める。これは候補チェーンを探す処理ではない。RFC 4158が説明するパス構築は、対象証明書から信頼アンカーまでの候補を得る仕事である。RFC 9618 は、候補パスが与えられた後のポリシー処理を更新する。

従来の状態は valid_policy_tree だった。各ルートから葉までの枝が一つのマッピング経路を表す。問題は、複数の発行者ポリシーが同じ主体ポリシーへ写像されると、同じ論理状態が経路ごとに複製されることにある。次の深度では、その複製ごとに子ノードも複製される。

各中間証明書に二つの OID があり、両方が次の二つへ完全に写像される場合、ツリーは深度ごとに倍増する。ネットワークで受け取るデータは少しずつ増えるだけなのに、内部表現は指数的に増える。TLS の相互認証であれば、攻撃者は認証に成功せずとも検証容量を奪える。

RFC が挙げる CVE-2023-0464 と CVE-2023-23524 は、この境界が実装事故になったことを示す。OpenSSL のコミット記録はポリシー検査の過大な資源利用とノード数制限を記し、Apple のセキュリティ情報は悪意ある証明書の処理によるサービス拒否を記す。OpenSSL のリリース履歴にも緩和策が残る。

到達可能性を保ち、経路の重複を捨てる

RFC 9618 の valid_policy_graph は、証明書の深度ごとにノードを分けた有向非巡回グラフである。同じ深度の同じポリシー OID は一つだけ存在する。前段の複数ポリシーから到達する場合は、その一つのノードが複数の親を持つ。子孫も一度だけ保持される。

意味は失われない。古いツリーは、グラフのルートから葉までの全経路を列挙したものと考えられる。最終的に到達できるポリシーを知るために、全経路を実体化する必要はない。新しい手順はパスの有効・無効と最終的な有効ポリシーを変えない。

変わるのは上限である。グラフの大きさはチェーン内のポリシーとマッピングの総数に対して線形に制約される。大きな入力が無料になるわけではないが、小さな組合せ入力が指数的な内部オブジェクトを作る非対称性は除かれる。

共通規格はここで必要最小限に留まる。anyPolicy、制約カウンター、マッピング、枝刈り、最終集合の意味を固定し、メモリ配置までは中央から命じない。各実装は意味と上限を守る限り、局所的に設計できる。

出力が旧リスクを復元する

RFC 5280 は完全な valid_policy_tree を検証結果の一部としていた。内部で安全なグラフを使っても、古い呼出側が完全なツリーを要求すれば、共有ノードを経路ごとに展開しなければならない。脆弱な表現は出力段階で戻ってくる。

RFC 9618 はこの出力を非推奨とし、権威側および利用者側の制約を反映したポリシー集合を返すよう勧告する。多くの業務判断に必要なのは「どのポリシーが有効か」であり、その結論へ至る全ての同値経路ではない。

互換性のための遅延再構築は可能だが、遅延は計算量を削減しない。危険な支払い時点を先送りするだけである。残すなら、共有認証経路から分離し、割当量、レート制限、監視、責任者を明記する必要がある。

ここでは古い API の利用者が実質的な統治者になり得る。所有者のいない診断機能が全認証の可用性を左右するなら、互換性の範囲が広すぎる。必要な出力を確認し、習慣を要件に昇格させないことが重要だ。

制限値が作る新しい拒否境界

グラフへ移行する前の緩和策として、チェーン深度やツリーノード数を制限できる。しかし深度が低すぎれば正当なチェーンを拒否し、高すぎれば分岐を許す。RFC 9618 は、証明書ごとのポリシー数を増やすと、深度制限下でもおよそ O(N^(深度/2)) の増加が残ると説明する。

ノード上限にも挙動の証明がいる。どの時点で止まり、どのエラーを返し、その時点までにメモリと CPU をどれだけ使い、再試行が何を起こすか。制限の整数だけでは、他の利用者を守ったか判断できない。

ポリシー検証を無効にすることも万能ではない。関連拡張が critical なら、処理できない実装は未認識の critical 拡張として証明書を拒否しなければならない。意味を無視して資源だけ節約する選択肢はない。

二種類の証拠を同じベクトルから得る

意味の試験は、通常ポリシー、複数マッピング、anyPolicy、明示ポリシー要求、マッピング禁止、anyPolicy 禁止、枝刈り、空の最終集合を含める。成功・失敗だけでなく、利用者制約後の最終集合まで比較する。複雑な正当ケースを一律拒否する高速化は等価ではない。

計算量の試験は、深度、証明書ごとの OID 数、マッピング密度を別々に変える。入力バイト、グラフのノードと辺、最大メモリ、割当回数、CPU 時間、経過時間、拒否理由、同時実行時の待ち行列を記録する。

両者は同じ試験記録で結び付けるべきだ。意味ケースには資源トレースを、敵対ケースには正確な判定を残す。そうしなければ、意味を変えた速度向上と、危険を残した互換性の両方が成功に見える。

実行中のバージョンも証明対象である。ロードされたライブラリ、ビルド設定、ポリシー機能の有効状態、旧ツリー API の呼出し、処理プロセスを記録する。RFC の発行は利用可能性を作るが、配備を証明しない。

RFC 9618 は X.509 全体の計算量を解決する文書ではない。パス探索、署名、失効確認、解析、アプリケーション認可には別の境界がある。ここで閉じるのはポリシーマッピングの特定の増幅である。その限定性こそ、測定可能な責任を作る。

出典