要約
- TLSの相互認証で使われた証明書を、プロキシがHTTPフィールドとして付け加えると、元のリクエストとは異なる大きさになる。手前の受け入れ成功は、奥の余白を保証しない。
- DERのバイナリーサイズ、HTTP圧縮を展開した後のテキストサイズ、通信量と動的テーブルの容量は別の尺度である。
- 追加情報の余白は、実際の経路と受信側に合わせて確保する必要がある。証明書やチェーンを黙って削ることは、同じ意味のリクエストを小さくすることとは限らない。
通信量の改善だけでは答えが出ない
仮に、クライアント証明書を転送する内部接続で、フィールド圧縮の効率が改善したとする。送信したバイト数は減る。しかし、受信側が再構成するフィールドの名前と値は同じなら、非圧縮のフィールド全体にかかる上限への負担も同じままである。通信の費用とメッセージの受け入れ条件は一致しない。
これは実施した試験や観測した障害ではなく、尺度を区別するための仮定である。改善を示す一つの数字が、別の制約も満たした証拠として使われることを避けたい。圧縮されたブロックが小さくなったという情報は有用だが、それだけでアプリケーションの許容範囲を説明することはできない。
RFC9440は、TLSを終端するリバースプロキシが、元のクライアントの証明書情報をオリジンサーバーへ伝える仕組みを記述する。2023年7月にIETFのInformational、すなわち情報提供文書として公開された。Internet Standards Trackの仕様ではなく、既存の慣行を整理して独立した実装の相互運用を容易にすることが目的である。
対象は、クライアントと終端プロキシの相互認証されたTLS接続で提示された証明書である。途中に別のプロキシやTLS接続があっても、フィールドが表す証明書を最後の内部接続のものに読み替えるわけではない。バックエンドは元の情報を利用できるが、元のTLS接続そのものがバックエンドまで続くわけでもない。
本稿では、その信頼境界を改めて一から論じるのではなく、情報を次の形式へ移す際の容量を扱う。信頼できる経路であることと、その経路の受信側が十分な大きさのリクエストを処理することは別条件である。正しく得られた情報にも、転送形式のための余白が要る。
リクエストは受信後にも大きくなる
別の仮定として、クライアントの元のリクエストがプロキシの許容範囲内だったとする。TLSの相互認証も成立している。プロキシは転送前に証明書フィールドを加える。オリジンサーバーが受け取るフィールドの集合は、クライアントが送ったものより大きくなり得る。
その追加後の集合が受信側の上限を超えても、外側のTLS認証が失敗したとは限らない。メッセージの表現と、受け入れる主体が変わったところで別の制約に達したのである。この経路上の増分を考えなければ、「クライアントは許された大きさで送った」という説明と「アプリケーションは受け取れない」という説明が衝突して見える。
両者は両立する。プロキシは受け入れ側であると同時に情報の追加側でもあり、オリジンサーバーは追加後の受け入れ側である。元のメッセージの大きさだけを確認しても、追加を約束した参加者の費用が抜け落ちる。TLSの成功とHTTPリクエストの容量契約を一つの結果に縮めてはいけない。
圧縮の展開とbase64のデコードは違う
RFC9440の証明書は、DERで符号化されたX.509オブジェクトをStructured FieldsのByte Sequenceとして表現する。テキスト上ではbase64をコロンで囲み、改行や空白を挟まない。このため、元のバイナリーの大きさを、そのままHTTPフィールド値の長さとして扱うことはできない。
base64への変換、区切り、フィールド名、リストの構文はそれぞれ容量を消費する。符号化に伴う長さの関係は計算できるが、計算結果がすべての実装の受信義務になるわけではない。表現の長さを求めることと、実際のサービスの上限を選ぶことは別の判断である。
HPACKやQPACKの展開で得られるのは、フィールド名とテキストの値である。そこで証明書のbase64までDERへ変わったわけではない。後でその値をバイナリーへデコードする段階と、HTTPフィールドの非圧縮サイズを数える段階を区別する必要がある。
さらに、圧縮表の参照が短くても、展開された値は元の長さを持つ。リテラルとして送って表に残さなくても、値はリクエストに存在する。通信上の表現、保持する状態、処理するテキスト、元の証明書を一つの「サイズ」にまとめると、誰がどの制約を守るべきか分からなくなる。
任意のチェーンにも、選んだ意味がある
Client-Certはエンドエンティティー証明書を含むリクエスト専用のフィールドであり、単一の値でなければならない。リストにしたり、複数回置いたりしてよいわけではない。これに対してClient-Cert-Chainは任意のByte Sequenceリストで、TLSの証明書順序に従う。すでに前者に入れた証明書を重ねて含めず、前者なしでは現れない。
任意という性質は、どの配置を採用するかを選べることを意味する。チェーンを受け取る前提のアプリケーションが、容量対策で受け取れなくなっても同じ入力を得たことにはならない。少ないバイト数で送れたという結果と、同じ意味を保てたという結果は区別する必要がある。
RFC9440は、送信先のオリジンが省かれる信頼アンカーを持つと分かっている場合、ルート証明書の省略を認める。省きたい理由が単にサイズ超過であるだけでは、その条件を満たさない。必要なチェーン要素を順に削って枠へ収める一般的な許可でもない。条件は特定の受信側に関するものだからである。
チェーンのリストを複数のフィールド行へ分けることも認められ、仕様は圧縮上の利点の可能性に触れている。一方、フィールドサイズの計算では行ごとに名前と所定の追加分が数えられる。ある表現が圧縮の再利用に向いていても、別の計算の合計を増やすことがある。どの尺度が受け入れを決めるのかを知る前に形式だけを最適化すべきではない。
公表された上限は経路全体の保証ではない
RFC9110は、HTTPのフィールド行、値、セクション全体に共通する既定の最大長を置いていない。受信側は自らの制約を持ち、処理したい量を超えるリクエストフィールドには適切な4xx応答を返す必要がある。大きすぎるフィールドを黙って無視するとリクエストスマグリングへの弱さを増すという警告もある。
HTTP/2では、RFC9113のSETTINGS_MAX_HEADER_LIST_SIZEを使う。非圧縮の名前と値のオクテット数に、フィールド行ごとに32オクテットを加えて数える。この設定は参考情報として通知するものであり、受信側は特定のリクエストに、通知した値より小さい限度を適用してもよい。初期値が無制限でも、アプリケーションや経路の実容量が無制限と分かったわけではない。
HTTP/3の名前はSETTINGS_MAX_FIELD_SECTION_SIZEである。RFC9114でも、非圧縮の名前と値に各フィールドの32バイトを加えて計算する。設定を受け取った相手はそれを超える送信を避けるべきだが、仕様は各実装が別々に制限を適用することも明示する。一つの通知値を下回っても、途中や奥の実装が受け入れる保証にはならない。
名称をHTTP/2とHTTP/3で取り違えないことも重要である。いずれも圧縮されたフレームの大きさそのものではない。小さく転送されたものが大きく展開されることと、経路の先で別の限度があることを、設定の確認から除いてはいけない。「どの実装が、どの表現を、どのリクエストについて数えるか」が必要な情報である。
バイナリー解析の最低能力とは別の話
Structured FieldsのRFC8941と後継RFC9651は、Byte Sequenceのパーサーに、バイナリーデコード後で少なくとも16,384オクテットを扱う能力を求める。これはそのデータ型の解析能力であり、そのサイズの証明書を含むHTTPメッセージ全体を必ず処理する約束ではない。
HTTP圧縮を展開した段階ではbase64の値が残り、他のフィールドやチェーンの構文もある。最低能力の数字をそのままテキスト上限に移せば、対象と単位の両方を変えてしまう。必要な解析能力を持ちながら、あるリクエストには小さい運用枠を設けることは両立する。
逆に、大きなメッセージを受け入れたことは証明書の妥当性やアクセス権の証明にならない。パーサーの能力、フィールドの量、オブジェクトの検証、アプリケーションの許可は別々の条件である。容量の議論は、そのどれかを他の条件の代わりにしないところから始まる。
追加する前に、追加する分を空ける
プロキシがバックエンドの上限をそのままクライアントへ通知すれば、証明書を追加する前に枠を使い切られる可能性がある。RFC9440は、最大ヘッダーサイズを知らせる中間者に対し、自らの追加分を考慮して十分に小さい値を上流へ通知するよう勧める。これは余白の設計であり、どこでも同じ量を差し引く公式ではない。
何を追加し、どの単位で数え、誰に届けるかを、実際の経路に沿って決める必要がある。別の検証チェーン、シリアライズ方式、宛先、通常のリクエストフィールドが、見積もりを変えることはあり得る。ここではいずれも未実施の想定であり、実際の移行を報告しているのではない。
最小限の共通の数え方があっても、将来のすべての追加が同じ大きさになるとは限らない。局所的な合意には、見積もりの前提と見直す条件が要る。一つのリクエストが通ったことを、すべての経路と将来の情報量への予約にしてはならない。
テーブルを大きくする選択と、値を残さない選択
RFC9440が圧縮効率について述べるのは、異なるクライアントのリクエストを、圧縮可能な同一の内部接続へ多重化する場合である。大きく変化する証明書値が効率を損ねることがある。全実装の性能が悪いという観測でも、一律の対処を求める規則でもない。
オリジンの動的テーブルを大きくする方法と、プロキシが不十分なテーブルに値を入れずリテラルで送り続ける方法が、可能な取捨選択として示される。繰り返しの多い値と、異なる値の連続では再利用の機会が違う。保持する状態と送る量の釣り合いを、対象の負荷に即して判断する余地が残されている。
HPACKでは、RFC7541がHuffman符号化前の名前と値に32オクテットを加えて表項を計算する。許される最大容量はプロトコルに制約され、エンコーダーはより少ない容量も選べる。これを増やしても、アプリケーションのフィールド受け入れ枠が自動的に増えるわけではない。
QPACKの最大動的テーブル容量は、デコーダーのSETTINGS_QPACK_MAX_TABLE_CAPACITYによって制限される。RFC9204では、最大値がゼロなら表項を挿入できない。HTTP/3のフィールド全体の上限とは違う設定である。動的表への挿入がないことを、すべてのフィールド圧縮が不可能だという意味に広げたり、証明書リクエストが必ずブロックされると断定したりすべきではない。
小さくする処置は意味の変更にもなり得る
RFC6585の431は、受信側が大きすぎるリクエストフィールド全体、または特定のフィールドを処理したくない場合の状態コードである。フィールドを減らした再送が認められ、個別の原因ならそのフィールドを示すことが推奨される。ただし使用は任意であり、攻撃時の接続破棄なども想定される。容量超過のすべてに431が見えるという保証ではない。
クライアントが追加分を制御できなければ、同じ再送は余白を増やさない。証明書を削れば小さくなるかもしれないが、アプリケーションの認証入力を変える可能性がある。証明書フィールドがない正当な配置もあり得るため、常に拒否する規則を提案しているのではない。欠如をどう扱うかを、容量処置に黙って決めさせないことが重要である。
セッション再開時の一貫性も別途選ぶ必要がある。一部のTLS実装は再開時に証明書情報を保持できない。RFC9440は、値を一貫して提供できない場合、証明書接続で再開を無効にするか、後で用意できないフィールドを最初から省くことを勧める。条件付きの設計の選択肢であり、現行サービスへの変更指示でも、全再開が情報を失うという主張でもない。
最後に、応答キャッシュと圧縮表は別である。Client-Certで応答を選ぶ場合、RFC9440は保存不可とするか、Vary: Client-Certによって同じ値のリクエストだけへ再利用を限定する。終端プロキシがVaryに証明書フィールドを見たら、その値を*へ変えてユーザーエージェントのキャッシュを防ぐことが推奨される。431応答はキャッシュ保存できない。表容量の拡大はこれらの代わりにならない。
経路の信頼、注入されたフィールドの除去、意図しない閲覧や改変からの保護は、容量とは独立に必要である。収まるリクエストが安全なリクエストと同義になるわけではない。情報の追加を引き受ける配置には、信頼の契約と容量の契約の両方が要る。
出典と分析の限界
本稿は公開仕様とLu Hengの調整原則を用いた分析であり、運用の計測ではない。例は仮定で、試験は未実施である。ベンダーの既定値、安全な一律の上限、障害、定量的な性能改善、完了した否定試験は主張しない。設計上の示唆は、仕様の要求事項と分けて述べている。
会員向け解説
プロフィールの詳細
適切な会員レベルでログインすると、解説全文と出典メモをご覧いただけます。
Strategic Circle 限定
Strategic Circle
すべての読者に公開されています。参加してログインすると プロフィール解説 を閲覧できます。
Strategic Circle に参加Leadership Alliance 会員限定
Leadership Alliance
対象となる IP 資産の所有者・管理者向けです。ログインすると Leadership Alliance の解説を閲覧できます。
Leadership Alliance に参加
