[129] HPACK は、HTTP/2 のヘッダーで採用されている圧縮形式です。 RFC 7541 で定義されています。
[5] ヘッダーリストは、 ヘッダー欄の順序付きリストです。 >>1
[28] 重複するヘッダー欄を含むことができます。 HTTP/2 header block に含まれるヘッダー欄の完全なリストは、 ヘッダーリストです。 >>1
[7] ヘッダーブロックは、 ヘッダー欄表現を連結したもので、復号すると完全なヘッダーリストが得られるものです。 >>1
[37] ヘッダーブロックの先頭には動的表サイズ更新があるかもしれません。
[11] 符号化器は、入力のヘッダーリストを符号化してヘッダーブロックとして出力するものです。 フレームに含めるデータの生成に用います。 符号化器は、次のようにします。
[49] 復号器は、入力のヘッダーブロックを復号してヘッダーリストとして出力するものです。 復号エラーを返すこともあります。 フレームに含まれるデータの解釈に用います。 復号器は、次のようにします。
[113] なおヘッダーブロックの各内容は、先頭ビットにより次のように判断できます。
[2] ヘッダー欄は、名前と値の組です。 いずれも不透明なオクテット列として扱います。 >>1
[6] ヘッダー欄表現は、 ヘッダー欄を索引表現またはリテラル表現によって符号化したものです。 >>1
[9] 符号化器は、ヘッダー欄をいずれかの方法でヘッダー欄表現として符号化しなければなりません。 その選択の方法は実装に任されていますが、ヘッダー名や値の性質によって、 セキュリティー上問題の無いように決定する必要があります。
[125] 索引付けを行うリテラル表現により符号化する場合は、符号化に加えて、 必要なら eviction を行ってから >>1、 ヘッダー欄を動的表の先頭に追加 (できれば) します >>1。
[29] 復号器は、ヘッダー欄表現を次のようにしなければなりません。
[114] 中間器の実装に用いる符号化器と復号器は、 通常の名前と値の組に加えて、決して索引付けしないリテラル表現であることを保存しなければなりません (>>86)。
[19] 索引表現は、静的表または動的表上の項目を参照する形でヘッダー欄を表しています。 >>1
[64] 索引表現は、最初の1ビットが 1
でその後の7ビットを接頭辞とする整数
(>>50) で構成され、この整数が索引 (>>17) を表します。 >>1
[20] リテラル表現は、 名前と値の組によりヘッダー欄を表します。 名前は、文字列リテラルまたは索引により記述します。 値は、文字列リテラルにより記述します。 >>1
[26] 索引付けを行うリテラル表現では、次のようにします >>1。
[71] 索引付けを行わないリテラル表現では、次のようにします >>1。
[86] 中間器は、転送時に絶対に索引付けしない (0001
)
表現で符号化されたヘッダー欄は、
絶対に索引付けしない表現のまま符号化しなければなりません >>1。
[148] どの表現方法を選択するかは実装に委ねられています。
[149] Firefox は、文字列を表す際に、常にハフマン符号化するように見えます。
[151] Chrome は、文字列を表す際に、短ければそのまま、長ければハフマン符号化するように見えます。
[150] Firefox は、 cookie
ヘッダーを送るときに値が短ければ決して索引付けしないとし、
長ければ索引付けするように見えます。短いかどうかをどう判断しているのかはよくわかりません。
:path
ヘッダーは索引付けしないで送信するようです。
その他常に送信するヘッダーは、索引付けするようです。
[152] Chrome は、 :path
は索引付けしないとし、
それ以外で常に送信するヘッダーは索引付けするように見えます。
[153] Firefox は authorization
ヘッダーを決して索引付けしないとするようです。
Chrome は索引付けするようです。
[119] 表は、項目の順序を持った列です。項目は、 索引によって識別され、ヘッダー欄の名前と値を表しています。
[4] 静的表 >>103 は、 頻出ヘッダー欄を集めたものです。静的表は読み取り専用で、 あらゆる場面で共通であり、いつでも使うことができます。 >>1
[3] 動的表は、 特定の接続で重出する (かもしれない) ヘッダー欄を追加するものです。 初期状態は空ですが、符号化や復号により追加 (や削除) されてゆきます。 >>1
[15] 動的表は、重複する (同名同値の) 項目を含むことがあります。 復号器はこれをエラーとしてはなりません。 >>1
[14] 動的表は、接続ごとに異なるものを使用します >>73。
[122] 動的表は、双方向通信の符号化 (送信/圧縮) 用と復号 (受信/展開) 用で異なるものを使用します >>1, >>73。
[128] 実装によっては、より多くの情報を動的表の項目に付加し、符号化時に参照するかもしれません (>>93)。
[17] 表中の項目は、索引により参照されます。
[13] 動的表と静的表は、どちらも共通の索引番地空間上にあります。
1
以上、静的表の大きさ以下の索引は、
静的表上の項目を表しています。それよりも大きな索引は、
動的表上の項目を表しています。 >>1
[121] 動的表は、 FIFO であり、最初・最新の項目ほど小さな索引を持ち、 最古の項目ほど大きな索引を持ちます。 >>1
[18] 両表の長さの和よりも大きな索引は、 復号エラーとしなければなりません >>1。
[65] 索引表現における値 0 は、復号エラーとしなければなりません >>1。
[145] HTTP/2 における動的表の理論上最大サイズは 232-1 です (>>144)。 ヘッダーは名前も値も空の時 (実際には HTTP/2 では不可) 最小サイズ32 (>>35) なので、動的表の最大項目数は 227。これに静的表の個数を加えたのが索引の最大値です。 従って24ビット整数では表せませんが、32ビット整数なら十分です。
[34] 動的表のサイズは、各項目のサイズの和です。 >>1
[35] 項目のサイズは、名前のオクテット長と値のオクテット長と 32 の和です。 >>1
[10] 動的表には、メモリー消費の制約ということで最大サイズが設けられています。 符号化器は、動的表サイズを最大サイズ以下としなければなりません >>1。復号器は、動的表サイズを最大サイズより大きくすることはありません。
[16] 最大サイズに達すると削除 (eviction、>>44) が発生するため、 古い項目は符号化や復号に使えなくなります。そのため、 符号化器が最大サイズを変更したら復号器に通知する必要があります。 動的表の最大サイズの変更は、動的表サイズ更新により通知されます。 動的表サイズ更新は、変更の後の最初のヘッダーブロックの最初になければなりません。 >>1
[41] HTTP/2 では、変更の後とは設定の acknowledgment の後に当たります。 >>1
[42] 2つのヘッダーブロックの転送の間に最大サイズの変更が複数回行われた時は、 最小サイズを動的表サイズ更新で通知しなければなりません。 更に最後のサイズも動的表サイズ更新で通知しなければなりません。 動的表サイズ更新は、この高々2つとなります。 >>1
[147] Firefox も Chrome も、動的表サイズ更新がどの位置に何個あってもエラーとはしないようです。
[80] 動的表サイズ更新は、先頭の3ビットが 001
で、
その後5ビットを接頭辞とする整数によって新しい最大サイズを表します >>1。
SETTINGS_HEADER_TABLE_SIZE
[81] 動的表の新しい最大サイズは、プロトコルによる上限以下でなければなりません >>1。
[82] HTTP/2 では、復号側から受信し、符号化側が acknowledge した最新の設定
SETTINGS_HEADER_TABLE_SIZE
の値がこの上限です。 >>1
[83] 上限を超える値は、復号エラーとしなければなりません。 >>1
[124] 最大値の初期値はプロトコルの上限と思われます。
[132] HTTP/2 の設定 SETTINGS_HEADER_TABLE_SIZE
(0x1
)
は、受信者側がヘッダーブロックの復号に使う表の最大サイズをバイト単位で送信者側に通知するものです。 >>84
[134] 符号化器は、本設定で指定された値以下のサイズの表を使うことができます。 >>84
[133] 本設定の初期値は、 4096 バイトです。 >>84
[144] HPACK の索引は HPACK 整数なので、任意の大きさの値を使えます。
つまり HPACK として動的表のサイズはいくらでも大きくできます。
HTTP/2 の SETTINGS
フレームにより指定できる値の最大値は
232-1 で、本設定固有の上限は設けられていないようです。
[44] 最大サイズが減少した時は、動的表のサイズが最大サイズ以下となるまで、 末尾から順に削除 (eviction) します。 >>1
[45] 新しい項目を追加する前には、動的表に新しい項目のサイズを足しても最大サイズ以下となるか、 表が空となるまで、末尾から順に削除 (eviction) します。 >>1
[46] 新しい項目が最大サイズ以下なら、動的表に追加します。 >>1
[48] なお、新しい項目は、それによって動的表から削除される項目の名前を参照していることがありますから、 注意が必要です。 >>1
[56] オクテット列は、ヘッダー欄の名前や値に使います。 >>1
[57] 文字列リテラルは、オクテット列として直接、 またはHuffman符号により符号化します。 >>1
[146] 理論上は整数が任意の長さを扱えますから、文字列も任意の長さとできます。 実際にはメモリー容量や実装の都合などの制約を受けます。例えば 232-1 や 231-1 が文字列長の上限かもしれません。
[62] Huffman符号化の場合、 RFC 7541 の表 >>105 によるHuffman符号によって符号化し、 各オクテットに対応する符号をビット単位で順に連結したものです。 >>1
[108] この符号化の表 >>105 は、0-255 のオクテットと 256 = EOS (end-of-string) の257種類の記号に対して、5-30ビットの符号を割り当てるものです。 >>1
[107] オクテット境界でちょうど終わらない場合、次のオクテット境界まで詰めを挿入します。 文字列リテラルの一部と誤解されないよう、 EOS (end-of-string) 記号に対応する符号の最上位側数ビットを使います。 >>1
[63] Huffman符号化の復号時には、末尾の不完全な符号は、 詰めとみなして捨てます。8ビット以上の詰めは、復号エラーとしなければなりません。 EOS 記号に対応する符号の最上位側数ビットでない詰めも、 復号エラーとしなければなりません。 EOS 記号を含む文字列リテラルは、復号エラーとしなければなりません。 >>1
[51] 整数は、オクテット中のどこからでもはじめられますが、 必ずオクテットの最後で終わります。 >>1
[52] 整数は、開始オクテットのうちの開始以後の部分である接頭辞と、 その後の0個以上のオクテットの列で構成されます。 接頭辞のビット数 N は、文脈によります。 接頭辞の後に続くオクテットは、 MSB が1のものが続いた後、 MSB が0のもので終わります。 >>1
[54] 本方式の整数は無限に大きな値を表現できますし、 無駄に多くのオクテットを消費したり、桁溢れさせたりするかもしれません。 実装は (使われる文脈毎に適当な) 値やオクテット長の制限を超えたら、 復号エラーとしなければなりません。 >>1
[130] HTTP/2 ヘッダーブロックの復号エラーは、接続エラー
COMPRESSION_ERROR
としなければなりません >>73。
[91] 誤り符号 COMPRESSION_ERROR
(0x9
)
は、接続のヘッダー圧縮文脈を維持できないことを示します >>90。
[103] ヘッダー圧縮は、無駄な処理をさせるために濫用できます。
エンドポイントは利用状況を監視して制限するべきです。
接続エラー ENHANCE_YOUR_CALM
としても構いません。 >>104
[142] Chrome は仕様通りのようです。 Firefox はストリームエラー
PROTOCOL_ERROR
とするようです。
[143] Chrome も Firefox も、 HEADERS
の復号エラーを検出するより前に次の
CONTINUATION
のエラーを検査するように見えます。
[85] CRIME攻撃のように (暗号化されていても) 長さから内容を推定することを難しくするため、 HPACK では個々の文字レベルではなく、文字列全体を表に入れて参照する形を採っています。 しかしこれで完全に攻撃を防げるわけではありませんから、注意が必要なことには変わりありません。 >>1
[88] 機密データと攻撃者が何らかの形で注入可能なデータが同じ表に基づき圧縮されると、 攻撃が可能となります。同一の要求や応答に含まれるヘッダーの場合はもちろん、 HTTP/2 では同一の接続で同じ表を使いますから、 同じ接続を共有する他の要求や応答でさえあれば、 攻撃できてしまうかもしれません。
[89] 例えば、次のような状況が存在し得ます。
[93] 複数の出自を持つヘッダーを圧縮する場合は、動的表の項目を出自ごとに分け、 他の項目は使わないようにするのが理想的です。 >>1
[94] 圧縮性能の向上のため、一部は共通にしても構いません >>1。
[95] 例えば Webブラウザーは Accept-Encoding
の値を共通にできます
>>1。Webブラウザーが Accept-Encoding
ヘッダーに指定する値はどの起源でも同じでしょうから、
おそらく問題とならないはずです。
[96] 同じヘッダーに多数の異なる値を指定して推定を試みているらしき状況なら、 それ以後そのヘッダーで動的表を用いないようにしても良さそうです。 (ただ単に動的表から削除するだけでは、別の方法で簡単に再追加されてしまうかもしれませんから、 不十分です。) 値が短い場合ほど、すぐに動的表を使わないようにするべきかもしれません。 >>1
[97] また、繊細なヘッダーは決して索引付けしないことにする (常に文字列リテラルで表現する) ことにもできます。 >>1
[100] 逆に、分かったとしてもあまり価値が無いヘッダーは、索引付けした方がいいかもしれません。 >>1
[101] 例えば User-Agent
はどのサーバーにも同じ値で送信するのが普通なので、
その値を特定しようとする企てにはさして意味がありませんから、索引付けしても良さそうです。>>1
[112] HTTP/2 ヘッダーリストの機密データと攻撃者が制御できるデータを (同じ圧縮辞書で) 一緒に圧縮してはなりません。データの出所が不明なら、 圧縮してはなりません。 >>111
[154] 1025071 – Don't index :path field in hpack ( 版) <https://bugzilla.mozilla.org/show_bug.cgi?id=1025071>
[155] Issue 524041 - chromium - Reject line-folding in HPACK - An open-source project to help move the web forward. - Google Project Hosting ( 版) <https://code.google.com/p/chromium/issues/detail?id=524041>