Hello there, ('ω')ノ
概要
HTTP/1.1 の chunked 転送 を使った挙動で、末尾の 0(ゼロチャンク)の後に正しい改行(CRLF)があるかどうかで挙動が大きく変わります。実際に「最後の 0 のあとに改行がないと smuggling が成立しない」理由は、チャンク終端の検出が CRLF に厳密に依存しているためです。本記事では非エンジニアにも分かるように、仕様上の要点と実務での確認方法をわかりやすく解説します。
なぜ 0 の後の改行が重要なのか(直感)
HTTP/1.1 のチャンク転送は「サイズ行 → データ → CRLF」を繰り返し、最後に 0(ゼロサイズのチャンク)で終わる、という決まったパターンに従います。受信実装はこのパターンを見つけて「ここでメッセージは終わり」と判断します。
したがって、終端の 0 行が CRLF で終わらない、あるいは 0\r\n\r\n の最後の空行が欠けていると、受信側は「まだチャンク転送が終わっていない」と考え、以降のバイト列を次のリクエストとして切り出さない(あるいは待ち続ける)ため、想定した差分応答(例:次のリクエストで 404 が返る)が観察できなくなります。
仕様のポイント(噛み砕いて)
チャンク転送の単純化された構造は次の通りです:
<chunk-size in hex>\r\n <chunk-data>\r\n ... 0\r\n ← 終端チャンク(サイズ0) [optional trailer headers]\r\n \r\n ← チャンク転送全体の終端(空行)
重要な点:
- チャンクサイズ行は必ず CRLF (
\r\n) で終わる。 - チャンクのデータのあとにも CRLF が来る。
- 全体の終わりを示すのは
0\r\n\r\nに相当するシーケンス(トレーラが無ければ最後の\r\nが空行になる)。
受信パーサはこの正確な区切りを見てチャンク処理を完了させるので、どれか一つでも欠けるとパーサの判定が成立しません。
成功例と失敗例(視覚的に)
成功する(終端が正しい)イメージ:
...x=1\r\n 0\r\n \r\n
失敗する(終端 CRLF が不足)イメージ:
...x=1\r\n 0 ← CRLF が無い(あるいは LF のみ)
見た目は似ていても、0 の直後に \r\n があるかどうかが全てです。多くの実装は LF (\n) だけや改行なしを許容しないため、失敗します。
実務的チェックポイント(Burp 等での検証)
- 改行が CRLF か確認する:Burp の Raw 表示や hex 表示で
\r\n(\x0d\x0a)が入っているかチェックする。見た目の改行だけでは LF と CRLF の違いに気づかないことが多いです。 - ゼロチャンク後に空行を入れる:必ず
0\r\n\r\nとなるようにする(トレーラがなければ空行は必須)。 - 余分な空白や文字を入れない:
0(スペース付き)や0\r\nのように余分な文字があると失敗することがある。 - ツールの自動修正を無効化:Burp の「Update Content-Length」など、自動でヘッダを書き換えるオプションをオフにして検証する。
- HTTP/1.1 を使う:HTTP/2 ではフレーミングが異なるため、必ず HTTP/1.1 でテストする。
なぜこれは重要か(防御・診断の観点)
- この動作の差は フロントとバックの実装差異を突く根本理由 に直結します。チャンクの終端が正しく処理されないと、期待した「バイト列の境界」が生じず、診断や防御の検証が誤る可能性があります。
- 防御側では、ヘッダの正規化(矛盾する
Content-LengthとTransfer-Encodingの同時存在を拒否)やフロントとバックで同一のメッセージ境界ルールを適用することが重要です。検証時には改行の正確さまで確認しましょう。
まとめ
- HTTP/1.1 の chunked では、終端は厳密に
0\r\n\r\nと表現される必要がある。 - 「
0の後に改行があるかどうか」が成功/失敗の分かれ目であり、CRLF の有無を誤ると受信側がチャンク終了を検出できず smuggling の検証に失敗する。 - 検証時は Raw 表示/hex 表示で改行コードを必ず確認し、ツールの自動補正はオフにしてテストするのが確実。
Best regards, (^^ゞ