要約

  • RFC 9729 は TLS exporter 由来の値に署名することで、認証チャレンジを待たず、最初の要求からクライアントが資格を証明できるようにする。
  • 証明は個々の HTTP 操作ではなく接続とオリジン文脈に結び付くため、接続再利用、TLS 終端、フロントエンドからバックエンドへの転送が認証の一部になる。
  • 外向けの応答を存在しない資源と揃えても、内部では失敗理由、鍵台帳の世代、接続、認可、実際の配信結果を分離して残さなければならない。

保護された運用画面があるとして、無資格の訪問者には通常の 404 を返す。資格のある端末は同じ URL に最初から Concealed 資格情報を付け、資源を受け取る。サーバーは「ここで認証できます」と先に告げない。クライアントは挑戦を手掛かりに資源の存在を確認できない。

RFC 9729 は、サーバーチャレンジが担っていた freshness を TLS の鍵素材 exporter に置き換える。これは秘密の URL を認証と呼ぶ設計ではない。利用者の秘密鍵、サーバー側の受理鍵台帳、接続に固有の値を組み合わせ、公開前の段階で証明を成立させる方式である。

鍵を持たせる権限は規格の外にある

規格は外部の鍵配布機構を前提とする。誰が加入資格を決めるのか、秘密鍵をどの端末に置くのか、退職・紛失・侵害をどう通知するのか、失効をどの時点で全検証点に反映したとみなすのかは決めてくれない。

この空白は周辺業務ではなく、第一の制御面だ。古い受理表に対する正しい署名は、古い権限を正確に証明する。管理画面で鍵を削除しても、ある gateway が以前の世代を読み続けていれば失効ではない。全経路で古い鍵が拒否され、有効な鍵が成功するところまで観測して初めて、変更は運用上の事実になる。

台帳には鍵そのものだけでなく、発行主体、利用者区分、対象オリジン、realm、公鍵 fingerprint、開始・終了時刻、失効理由、配布世代、受け取るべき検証点を記録する。秘密鍵を記録する必要はないが、「どの判断がどこまで届いたか」を証明できなければならない。

exporter の 48 byte が意味する範囲

クライアントは EXPORTER-HTTP-Concealed-Authentication という登録済みラベルを使う。文脈には TLS signature scheme 番号、鍵 ID、公鍵、URI scheme、host、port、必要なら realm が入る。48 byte の出力のうち 32 byte は署名入力に、16 byte は検証用の v に使われる。

Authorization の資格情報は k、a、p、s を運ぶ。サーバーは ID の存在、提示公鍵と保存公鍵の一致、v と当該接続 exporter の一致、署名を別々に調べる。提示公鍵の比較は key confusion を避け、検証 byte は特殊な鍵が複数入力を受理する余地を狭める。

ただし文脈は method、path、body まで含まない。同じ接続上では同じ鍵の証明を複数要求で再利用でき、圧縮には有利だが、別の隔離文脈から Authorization を読める主体は接続内で再送できる。HTTP/2 や HTTP/3 の多重化、ブラウザ文脈、tenant 間の proxy 隔離が、認証境界そのものになる。

freshness も「新しい/古い」の一語で済まない。証明は基礎接続と同じだけ古くなり得る。長寿命または再開された接続を許すなら、接続年齢と resumption の系譜を残す。より短い有効窓を求めるなら新接続を強制できるが、handshake 費用、容量、障害率との交換になる。

TLS 終端装置は証言者になる

TLS または QUIC を front end が終端し、鍵台帳を backend が持つ構成では、front end が元の Authorization と Concealed-Auth-Export を backend に渡す。backend 自身の接続からは、client–front end 間の exporter を再計算できない。したがって front end の申告を信頼するしかない。

規格は、backend が既に信頼した送信者からの field だけを使い、front end は client が注入した同名 field を転送してはならない、と定める。この瞬間、load balancer は単なる通信設備ではなく、認証成立に必要な値を証言する principal になる。

共通 subnet の ACL だけでは粒度が粗い。検証記録には認証された front end の identity、software と設定世代、client 側接続 ID、選択した virtual host、正規化した authority、保護された exporter 相関値、最終 backend route が必要だ。service mesh が header を落とす、重複させる、別の host で文脈を組み立てる、検証後に別 authority へ流す、といった事故は、外側ではすべて美しい 404 に見える。

五つの検査と、その後の認可

backend は必須 parameter を parse し、鍵 ID を引き、公鍵を比較し、検証 byte を比較し、署名を検証する。どれか一つでも失敗すれば、資格情報が無い場合と同様に扱う。全部通って初めて「認証済み」と判断できる。

それでも操作の認可は別だ。有効な contractor 鍵が一つの機能だけに許可されているかもしれない。account が停止中かもしれない。route の一部だけが古い policy を持つかもしれない。逆に、認証処理を通らない公開 route が残ることもある。署名結果、認可 policy、資源の実効結果を別の事実として保存し、client 側の probe と結合する必要がある。

同じ失敗を返すとは何を揃えることか

non-probeable とした資源では、認証失敗を本当に存在しない資源と同一に扱う。404 という数字だけでは足りない。body、header、cache、connection close、長さ、処理時間の分布が違えば探索信号になる。

署名検証には計算時間が掛かる。存在しない path は即時、保護 path は鍵検索と検証後に返るなら timing oracle が成立する。一部の 404 に固定 delay を入れるだけでも、その delay 自体が特徴になる。実験は同じ負荷とネットワーク条件で分布を比較し、index、sitemap、error message、client bundle、文書、analytics、DNS といった別経路も調べるべきだ。

誤植も実装証拠に影響する

RFC 9729 の errataには、規範文だけでなく実装版を記録すべき理由がある。Verified の erratum 8807 は、署名文脈の hex 例が HTTP Signature Authentication を encode していたのに対し、規範的な名前は HTTP Concealed Authentication であると訂正した。例をそのまま実装した相手との不整合は、外部からは一般的な認証失敗にしか見えない。

Reported の erratum 8843 は、s の整数 ABNF が非ゼロ一桁を許さない一方、本文は 0 から 65535 を許すという問題だ。採用した解釈と parser test を構成証拠に含め、状態の変化を追う必要がある。

IANA の scheme、HTTP field、TLS exporter label の登録は共通名を確立する。しかし、登録済みだから client が実装している、鍵発行が健全である、全 route が同じ policy を使う、とは言えない。文書上の権威と実行上の効果は別の層である。

外には一つの答え、内には異なる理由

設計すべき出力は二つある。公開面では未認証者が得る差を最小化する。非公開面では、malformed、unknown key、revoked key、公鍵不一致、exporter 不一致、署名不正、TLS 不適格、untrusted front end、認可拒否、誤 route、downstream failure を区別する。

理由 code を response や timing に戻してはならない。限定された correlation ID で接続・構成事実と結び、保存期間と閲覧監査を決める。negative control には通常の nonexistent path を必ず混ぜ、資格情報なし、破損、未知鍵、失効鍵、誤 host/port、古い TLS、client 注入 field、信頼外 front end、認証後の認可拒否を並べる。外からは必要な組だけが収束し、内部記録は明確に分かれるべきだ。

情報源