【実務・中級編】HTTPヘッダーインジェクションの脆弱性と対策 – アプリケーションセキュリティ & 安全な開発防御ガイド

HTTPヘッダーインジェクション:その「改行」がシステムを陥落させる

現場でインシデント対応をしていると、「まさか、そんなところで?」という箇所で足元をすくわれるケースによく遭遇します。その筆頭格がHTTPヘッダーインジェクションだ。

多くのエンジニアは「SQLインジェクション」には過敏だが、HTTPヘッダーに対しては驚くほど無頓着だ。ユーザーからの入力をそのままレスポンスヘッダーに流し込む。この「何気ない実装」が、実は攻撃者にシステムをコントロールする鍵を渡していることに気づいていない。

今日は、HTTPヘッダーインジェクションの本質と、それを今日から根絶するための実務的なテクニックを共有する。

—

1. なぜ「改行コード」だけでシステムが崩壊するのか

HTTPプロトコルにおいて、ヘッダーとボディは \r\n\r\n (CRLF) で区切られている。攻撃者はこの仕様を悪用する。

例えば、ユーザーの入力値が以下のようにヘッダーに反映されるコードがあったとしよう。

// 脆弱な実装例:ユーザー入力をそのままヘッダーにセット
header(“X-User-Name: ” . $_GET[‘name’]);

もし、攻撃者が name パラメーターに admin\r\nSet-Cookie: session=evil という文字列を送ったらどうなるか?

サーバーが生成するレスポンスはこうなる。

HTTP/1.1 200 OK
X-User-Name: admin
Set-Cookie: session=evil
Content-Type: text/html

(ボディ内容…)

ブラウザやキャッシュサーバーは、この改行によって「あ、ここでヘッダーが終わって、次からボディだな」と誤認する。これがHTTPレスポンス分割攻撃(HTTP Response Splitting)の正体だ。キャッシュ汚染(Cache Poisoning)を引き起こせば、特定のユーザーのセッションを乗っ取ったり、偽のコンテンツを全ユーザーにバラ撒いたりすることが可能になる。

—

2. 【実務編】言語別:安全な実装サンプル

一番の対策は「ユーザー入力をそのままヘッダーに出力しない」ことだ。しかし、どうしても動的な値を設定せざるを得ない場合は、以下の実装をテンプレートとして使ってほしい。

PHPでの対策

PHP 5.1.2以降であれば、header()関数は改行コードが含まれるとエラーを出す仕様になっているが、ライブラリ層で迂回されるリスクを考慮し、明示的なサニタイズを推奨する。

function set_safe_header($name, $value) {
// 改行コード(CR/LF)を強制的に除去
$sanitized_value = str_replace([“\r”, “\n”], ”, $value);

// 念のためヘッダー名もバリデーション
if (preg_match(‘/^[a-zA-Z0-9-]+$/’, $name)) {
header(“$name: $sanitized_value”);
}
}

Python (Flask) での対策

Flask等のフレームワークを使う場合、make_response を経由してヘッダーをセットするのが定石だ。

from flask import make_response, request

@app.route(‘/set-header’)
def set_header():
user_input = request.args.get(‘name’, ”)
# 改行コードを削除してセット
safe_value = user_input.replace(‘\r’, ”).replace(‘\n’, ”)

response = make_response(“Header Set”)
response.headers[‘X-User-Name’] = safe_value
return response

—

3. インフラ層での防御(Nginxの設定)

アプリケーションコードの修正が追いつかない場合、あるいは多層防御を構築したい場合、Nginxの ngx_http_headers_module ではなく、WAFやリバースプロキシで「不正なヘッダー」を遮断する。

Nginxで特定の異常なヘッダーを拒否する設定例だ。

nginx.conf の http または server ブロック内
改行コードを含むリクエストヘッダーを拒否する(デフォルトで有効だが明示的に意識する)
underscores_in_headers off;

さらに厳格にいくなら、ModSecurity などのWAFを導入し以下のルールを適用する
SecRule RESPONSE_HEADERS “[\r\n]” “phase:3,id:1000,deny,status:500,msg:’Header Injection Attempt'”

—

4. チーフエンジニアからの「鉄の掟」

最後に、現場で設計する際に守ってほしい3つのルールを伝える。

1. ホワイトリスト方式を貫け: ヘッダー値に動的な値を入れる際、それが期待される形式(英数字のみ、特定のIDのみ等)か確認するバリデーションを必ず通すこと。正規表現 ^[a-zA-Z0-9\-_]+$ 程度は必須だ。
2. ヘッダーへの直接注入を避ける: 可能であれば、ユーザーからの入力値をそのままヘッダーに含める設計自体を廃止しろ。「どうしても必要か?」を自問自答せよ。
3. WAFは最後の砦: アプリが脆弱でもWAFで防げることはある。しかし、WAFだけに依存してコードを放置するのは、鍵のかかっていない玄関で警備員を雇うようなものだ。

セキュリティは「魔法のツール」で解決できるものではない。泥臭いコードの積み重ねと、仕様への深い理解が、君たちのプロダクトを唯一守ることができる。

次回のコードレビューで、もしヘッダー操作を見かけたら、この「改行」のリスクを思い出してほしい。その一行の修正が、重大なインシデントを未然に防ぐことになるのだから。

コメント

タイトルとURLをコピーしました