HTTPヘッダーインジェクション:その「改行」がシステムを崩壊させる
現場でインシデント対応をしていると、意外にも忘れ去られているのが「HTTPヘッダーインジェクション」だ。最近のモダンなフレームワークは優秀だからと油断していると、足元をすくわれる。
HTTPヘッダーインジェクションは、攻撃者がHTTPレスポンスヘッダーの中に不正な改行コード(`\r\n`)を挿入することで、レスポンスを強制的に分割し、偽のコンテンツを挿入したり、キャッシュを汚染したりする攻撃だ。
「入力値をそのままヘッダーに含める」という、一見些細な実装ミスが、企業の信用を吹き飛ばすWebキャッシュポイズニングやセッション固定攻撃へと直結する。今回は、この「盲点」を確実に塞ぐための実戦的な防衛策を解説する。
—
攻撃の構造:なぜ「改行」が武器になるのか
HTTPの仕様において、ヘッダーとボディは「空行(`\r\n\r\n`)」で区切られる。攻撃者はこの仕様を悪用する。
例えば、ユーザーの入力値を `Location: [入力値]` というヘッダーで返すアプリがあったとしよう。ここで攻撃者が `\r\nContent-Type: text/html\r\n\r\n…` という文字列を送り込むと、レスポンスは以下のようになる。
HTTP/1.1 302 Found
Location: http://target.com
Content-Type: text/html
本来のレスポンスの後に、攻撃者が仕込んだ「偽のボディ」がブラウザに到達する。これがWebキャッシュサーバーを経由していれば、その偽コンテンツがキャッシュされ、無関係なユーザー全員が被害に遭うことになる。これが「ヘッダーインジェクション」の恐ろしさだ。
—
実務で使える防御策:ブラックリスト方式は捨てろ
「改行コードを削除すればいいんでしょ?」と考えがちだが、それは甘い。エンコーディングの差異や、環境ごとの挙動の違いを考慮すると、ブラックリスト方式は必ずどこかで穴が開く。
鉄則は、「ヘッダーにセットする値は、ホワイトリストで厳密に検証するか、そもそもユーザー入力を直接ヘッダーに含めないこと」だ。
Python (Flask) での安全な実装例
Flaskなどのフレームワークでは、レスポンスオブジェクトを操作する際に適切なバリデーションを挟むのが定石だ。
from flask import Response, request, abort
import re
def set_secure_location(target_url):
# 【重要】許可するホスト名やURLパターンを正規表現でホワイトリスト化する
allowed_pattern = re.compile(r’^https://trusted-domain\.com/.$’)
if not allowed_pattern.match(target_url):
# 異常な入力は拒否する
abort(400, “Invalid redirect target”)
response = Response()
# 意図的にヘッダーをセットする
response.headers[‘Location’] = target_url
return response
JavaScript (Node.js/Express) での安全な実装例
Expressを使用する場合、ミドルウェアでヘッダーの健全性を担保するのが効率的だ。
const express = require(‘express’);
const app = express();
// ユーザー入力がヘッダーに混入する可能性のある箇所を監視する
app.use((req, res, next) => {
// ヘッダーの値に改行コードが含まれていないかチェックする汎用関数
const isHeaderSafe = (val) => typeof val === ‘string’ && !/[\r\n]/.test(val);
const originalSetHeader = res.setHeader;
res.setHeader = function(name, value) {
if (!isHeaderSafe(value)) {
throw new Error(`不正なヘッダー値が検出されました: ${value}`);
}
originalSetHeader.apply(this, arguments);
};
next();
});
—
インフラレイヤーでの防御(Nginx/WAF)
アプリケーション側で防ぐのが理想だが、レガシーシステムや運用上の理由で改修が困難な場合、WebサーバーやWAFが最後の砦となる。
Nginxでは、`ngx_http_headers_more_filter_module` 等を利用して改行を含むリクエストを拒否できるが、最も確実なのは 「WAFでの正規表現フィルタリング」 だ。
AWS WAFのルール設定例:
- 条件: `Header` の値に `\r` または `\n` が含まれているか(Regex Match)
- アクション: `Block`
- 解説: これを実装するだけで、アプリケーションの脆弱性を突く攻撃の大半をエッジで無効化できる。
—
結論:エンジニアが忘れてはならないこと
HTTPヘッダーインジェクションを防ぐためのチェックリストを最後に残しておく。
1. ユーザー入力のヘッダーへの直接代入はNG: 原則、ユーザーの入力をヘッダーに含めることは避ける。どうしても必要な場合は、定数やホワイトリストによる変換を行うこと。
2. フレームワークの更新を怠らない: 現代の主流フレームワーク(Rails, Django, Spring, Express等)は、最新版であればヘッダーへの改行挿入に対してガードが掛かっていることが多い。
3. WAFは「最後の保険」: アプリの堅牢性が担保できない場合の応急処置として、エッジ側でのフィルタリングを検討する。
セキュリティとは、技術の積み重ねであると同時に「疑う心」の積み重ねだ。あなたの書いたその一行が、明日誰かの個人情報を守るかもしれない。そう意識するだけで、コードの質は劇的に向上するはずだ。頑張ってくれ。

コメント