HTTPヘッダーインジェクション:その「改行」がシステムを崩壊させる
現場でインシデント対応をしていると、意外なほど見落とされているのが「HTTPヘッダー」への信頼過多だ。多くのエンジニアが「POSTボディのバリデーション」には血眼になるが、URLパラメータやヘッダー情報を経由して、サーバーのレスポンスそのものを歪められるリスクについては、驚くほど無防備なシステムが多い。
特に「HTTPレスポンス分割攻撃(HTTP Response Splitting)」は、古い脆弱性だと思われがちだが、現代の複雑化したWebアプリケーションにおいて、キャッシュポイズニングやセッションハイジャックの入り口として依然として猛威を振るっている。
今日は、なぜこの脆弱性が恐ろしいのか、そして現場でどうやって「コードレベル」と「インフラレベル」の両面から封じ込めるのかを解説する。
—
1. なぜ「改行コード」が武器になるのか?
HTTPプロトコルは、ヘッダーとボディの境界を「CRLF(\r\n)」という2バイトの文字シーケンスで判断している。
もし攻撃者がユーザー入力に \r\n を忍び込ませることに成功し、それをサーバーがそのままHTTPレスポンスのヘッダーとして出力してしまったらどうなるか?
攻撃者は、「本来のレスポンスの後に、偽のレスポンスを付け加える」ことが可能になる。ブラウザやプロキシサーバーは、この改行を「別のレスポンスの始まり」と誤認し、攻撃者が仕込んだ悪意あるコンテンツを、あたかも正当なサーバーからの応答であるかのように処理してしまう。これがレスポンス分割攻撃の本質だ。
攻撃イメージ(概念的なPoC)
例えば、リダイレクトURLをパラメータで受け取る仕様のアプリで:
Location: /redirect?url=http://trusted.com\r\nSet-Cookie: session_id=hacked
サーバーがこの値をヘッダーにそのままセットすると、HTTPレスポンスは以下のようになる。
HTTP/1.1 302 Found
Location: /redirect?url=http://trusted.com
Set-Cookie: session_id=hacked <-- 攻撃者が捏造したヘッダー
Content-Type: text/html
(本来のコンテンツ)
この瞬間、ユーザーのブラウザは悪意あるセッションIDを強制的に保存させられる。これが「目に見えない改行」が招く悲劇だ。
---
2. セキュアな実装パターン:フレームワークを信じすぎるな
現代のフレームワークは賢いが、開発者が「ヘッダー名」や「値」にユーザー入力を直接連結するようなコードを書けば、いとも簡単に突破される。
Python (Flask) でのセキュアな実装例
Pythonなどのライブラリでは、ヘッダー設定時に改行コードが含まれていないか検証する機能を持つものが多いが、根本的な対策は「ユーザー入力をヘッダーに直接入れない」ことだ。
from flask import Response, request
import re
def set_custom_header(response, key, value):
# 【防御の鉄則】改行コード(CR/LF)が含まれていたら即座に拒否する
# 正規表現でヘッダーとして不適切な文字が含まれていないか厳格にチェック
if re.search(r'[\r\n]’, value):
raise ValueError(“ヘッダーに改行コードが含まれています。不正なリクエストです。”)
response.headers[key] = value
return response
使い方:ユーザー入力を受け取った直後にバリデーションを行う
@app.route(‘/set-lang’)
def set_lang():
user_lang = request.args.get(‘lang’, ‘ja’)
resp = Response(“Language set”)
set_custom_header(resp, ‘X-Language’, user_lang)
return resp
—
3. インフラでの防御:WAFとNginxでの多層防御
アプリケーションだけで全てをカバーするのは限界がある。インフラ層でも「ヘッダーへの不正な注入」をブロックする設定を入れておくのが、シニアエンジニアの流儀だ。
Nginx での不正リクエストブロック
Nginxの設定(nginx.conf)で、ヘッダーに改行コードが含まれるリクエストを門前払いする設定を記述する。
HTTPリクエストのヘッダーに改行コードが含まれる場合、Nginxレベルで拒否する
古いバージョンや設定では見落とされがちだが、これは非常に強力な防御策だ
ignore_invalid_headers on;
厳格なチェックを有効にする
underscores_in_headers off;
WAF(AWS WAF等)での対策
AWS WAFを使用しているなら、「String match conditions」ではなく、「Regex match conditions」を活用せよ。
- ルール対象:
http_header - 正規表現:
(\r|\n|%0d|%0a) - アクション:
BLOCK
これだけで、アプリケーションコードの脆弱性が突かれる前に、境界線上で攻撃を弾くことができる。
—
結論:セキュリティは「疑うこと」から始まる
HTTPヘッダーインジェクションを防ぐための最善の策は、「ユーザーからの入力を信頼せず、ヘッダーに付与する前に必ずサニタイズ(あるいは拒否)する」という一点に尽きる。
- ホワイトリスト化: 可能な限り、ユーザー入力そのものをヘッダーに使うのではなく、事前に定義した値(定数)とマッピングする。
- 例外処理: 改行コード(
\r,\n)を検知した場合は、エラーを返すのではなく、ログに記録して接続を切断する。 - フレームワークの活用: 独自実装ではなく、フレームワークが用意しているヘッダー設定用メソッドを使い、そのメソッドが内部でバリデーションを行っているかドキュメントを確認する。
セキュリティは、教科書の丸暗記ではなく「どうすればこのシステムを破壊できるか?」という攻撃者の視点を持ち続けることで維持される。今日紹介したコードは、今すぐ君のプロジェクトのプルリクエストに反映させてほしい。安全な開発は、こうした細部へのこだわりから生まれるのだから。
コメント