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

ヘッダーインジェクション:なぜ「たかがヘッダー」がサイトを乗っ取るのか

エンジニア諸君、日々コードを叩いていると、ついつい「HTTPヘッダーなんて所詮メタデータだろ?」と軽視したくなる気持ちはわかる。だが、セキュリティの世界で最も致命的な事故の一つは、この「軽視」から生まれる。

HTTPレスポンスヘッダーへの不正な改行文字(CRLF:\r\n)の混入を許すということは、「Webサーバーとブラウザの間の会話に、攻撃者が横から割り込んで偽の指示を書き込む」ことを意味する。これがHTTPレスポンス分割(HTTP Response Splitting)や、キャッシュ汚染(Cache Poisoning)の入り口だ。

今日は、教科書的な「バリデーションしましょう」という退屈な話ではなく、現場でどう防ぎ、どう実装すべきか、その「急所」を叩き込む。

—

1. 攻撃者が狙う「盲点」:PoCのメカニズム

HTTPヘッダーインジェクションは、Webアプリケーションがユーザー入力をそのままLocationヘッダーやSet-Cookieヘッダーに埋め込むことで発生する。

例えば、以下のようなPHPコードを想像してほしい。

// 脆弱な実装例(絶対に真似するな)
$redirect_url = $_GET[‘url’];
header(“Location: ” . $redirect_url);

攻撃者は、urlパラメータに改行コードを仕込む。
?url=http://example.com\r\nSet-Cookie: session_id=evil_id

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

HTTP/1.1 302 Found
Location: http://example.com
Set-Cookie: session_id=evil_id
…

ブラウザはこれを見て、「おっ、セッションIDが更新されたんだな」と勘違いし、攻撃者が指定した値を保存してしまう。これがセッション固定攻撃や、XSS(クロスサイトスクリプティング)への足がかりになる。「URLさえ安全ならいい」という思い込みが、システムを崩壊させる。

—

2. セキュアな実装パターン:防御の鉄則

HTTPヘッダーに動的な値を乗せる際の原則は「改行文字は絶対に許可しない」、これに尽きる。

PHPでの対策(header()関数を使う場合)

PHP 5.1.2以降、header()関数はCRLFの注入を検知するとエラーを出す仕様だが、それでもアプリケーション層で弾くのがプロの流儀だ。

/

  • ヘッダーに設定する値をサニタイズする関数

/
function sanitize_header_value($value) {
// 改行(CR/LF)を完全に除去
return str_replace([“\r”, “\n”], “”, $value);
}

$user_input = $_GET[‘url’];
$safe_url = sanitize_header_value($user_input);

// リダイレクト先が自ドメインか確認するチェック(オープンリダイレクト防止)も必須
if (strpos($safe_url, “https://my-trusted-site.com”) === 0) {
header(“Location: ” . $safe_url);
}

Python (Flask) での対策

Flask等のフレームワークは、標準でヘッダーへの改行混入をブロックしてくれるが、make_response等で手動生成する場合は注意が必要だ。

from flask import Response, request

@app.route(‘/redirect’)
def redirect_user():
url = request.args.get(‘url’, ”)

# 改行を含む入力を即座に拒否
if “\r” in url or “\n” in url:
return “Invalid Input”, 400

response = Response(status=302)
response.headers[‘Location’] = url
return response

—

3. インフラ側での「二重のガード」:Nginx/WAF

アプリケーション側の修正が漏れていても、インフラ側で弾くのが多層防御だ。Nginxのngx_http_headers_more_filter_moduleを使えば、不正なヘッダーを強制的にフィルタリングできる。

Nginx設定例:

不正な文字を含むリクエストをログに記録し、400を返す設定を検討する
ただし、基本はアプリケーション層での修正が最優先
location / {
# ユーザー入力がヘッダーに反映されるエンドポイントには
# WAF(ModSecurity等)でCRLF注入パターンを正規表現でブロックする
# 例: SecRule ARGS “(\r|\n)” “deny,status:400,msg:’CRLF Injection Detected'”
}

—

4. チーフからの教訓:実装の心得

結局のところ、脆弱性は「便利さ」と「セキュリティ」のトレードオフの隙間に潜んでいる。

1. 動的なヘッダー生成を避ける: URLリダイレクトなら、入力値を直接ヘッダーに入れず、DB上のIDやホワイトリストに基づいた「マッピング」を行うのがベストだ。
2. ライブラリを信じすぎるな: フレームワークが守ってくれると安心している時ほど、独自実装の落とし穴にはまる。
3. レスポンスを観察せよ: 開発環境でcurl -vを叩き、自分の投げたパラメータがどのようにHTTPヘッダーとして解釈されているか、常に自分の目で確認する習慣をつけろ。

セキュリティとは、派手なハッキングを防ぐことではない。「当たり前のことを、当たり前に、徹底してやる」ことの積み重ねだ。今日のコードから、改行文字のチェックを義務化してくれ。それが、君たちのプロダクトを守る盾になる。

コメント

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