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

HTTPヘッダーインジェクション:その「一行の油断」がシステムを崩壊させる理由

エンジニア諸君、お疲れ様。今日もコードを書いているか?

セキュリティの世界では「入力値はすべて疑え」と口酸っぱく言われるが、意外と盲点になりやすいのがHTTPレスポンスヘッダーだ。リクエストパラメータをそのままログに出したり、リダイレクト先のURLに組み込んだりする際、「どうせヘッダーだし、HTMLじゃないからXSSの心配はないだろう」と高を括っていないか?

それが命取りになる。今日は、CRLF(改行コード)を悪用した「HTTPヘッダーインジェクション」と、それが引き起こす悪夢のような攻撃シナリオについて、実務の視点から解説する。

—

1. なぜ「改行」が武器になるのか?

HTTPプロトコルにおいて、ヘッダーとボディは「CRLF(\r\n)」という連続する2つの改行コードで区切られている。攻撃者はこの仕様を逆手に取る。

もし、ユーザーの入力がHTTPレスポンスヘッダーの構築に使われていて、そこに %0d%0a(CRLF)を混入させることができたらどうなるか?

攻撃者は、ヘッダーの区切りを偽装し、「本来存在しないはずのヘッダーを追加する」ことや、「レスポンスボディを捏造して別のコンテンツを注入する(レスポンス分割攻撃)」ことが可能になる。

具体的な攻撃の仕組み(PoCの概念)

例えば、ユーザーの入力値を Location ヘッダーにそのまま反映させる不適切なコードがあるとしよう。

正常なレスポンス
Location: /index.php?ref=[ユーザー入力]

ここで、攻撃者が [ユーザー入力] に %0d%0aSet-Cookie: session_id=attacker_controlled_value を送り込むと、サーバーが生成するレスポンスはこうなる。

攻撃後のレスポンス
Location: /index.php?ref=
Set-Cookie: session_id=attacker_controlled_value

[ここに任意のコンテンツを挿入可能]

これにより、セッション固定攻撃(Session Fixation)や、Webキャッシュ汚染によるフィッシングサイトへの誘導が成立してしまう。ブラウザは「サーバーがそう言っているのだから正しい」と信じ込み、細工されたCookieを保存してしまうのだ。

—

2. セキュアな実装:防御の鉄則

インジェクションを防ぐ唯一の確実な方法は、「外部からの入力値を、決してそのままレスポンスヘッダーに出力しない」ことだ。

Python (Flask) での安全な実装例

PythonのFlaskなどでは、レスポンスヘッダーを直接構築する際に標準の関数を使うのが鉄則だ。make_response を使い、個別のヘッダーキーに対して値をセットすれば、改行コードが含まれていても適切にエスケープ(またはエラー)される。

from flask import Flask, make_response, request

app = Flask(__name__)

@app.route(‘/redirect’)
def secure_redirect():
# ユーザー入力を受け取る
target_url = request.args.get(‘url’, ‘/default’)

# 対策:許可されたドメインや形式のみを許可するホワイトリスト検証を行う
allowed_domains = [‘example.com’, ‘myapp.internal’]
if not any(d in target_url for d in allowed_domains):
target_url = ‘/error’

response = make_response(“Redirecting…”, 302)
# 以下の指定方法は、内部で改行コードを正しく処理する
response.headers[‘Location’] = target_url
return response

Nginx での防御設定(最後の砦)

アプリケーション側での修正が追いつかない場合、あるいは防御を多層化したい場合は、リバースプロキシでヘッダーの不正な改行を弾くべきだ。

nginx.conf の http, server, または location ブロックに記述
HTTPヘッダー内の改行を検知して拒否する設定
(Nginx 1.11.x 以降はデフォルトで強力に保護されているが、念には念を)

悪意のある文字が含まれるリクエストを 400 Bad Request で返す
if ($http_user_agent ~ “(\r|\n)”) {
return 400;
}

—

3. 実務現場で生き残るための「思考法」

いいか、脆弱性を見つけるのはツールでもできる。だが、「どこがビジネスロジック的に重要か」を見極めるのはエンジニアの仕事だ。

1. ヘッダー出力にはテンプレートを使え: 自分で文字列結合してHTTPレスポンスを作ろうとするな。フレームワークのAPIを信頼しろ。
2. ホワイトリスト検証を徹底しろ: 「何を入力して良いか」を定義するだけで、攻撃の9割は無効化できる。
3. WAFは「お守り」ではない: WAFは便利だが、アプリケーション自体の脆弱性が放置されていれば、いつか突破される。根本的な修正(コードの修正)を怠るな。

最後に。セキュリティとは「終わりのないパッチワーク」ではない。「設計の時点での潔癖さ」だ。コードをレビューする時、HTTPヘッダーにユーザーの影が見えたら、即座に手を止めろ。その一行が、明日のインシデントニュースにならないように。

さて、コードに戻ろう。質問があればいつでも来い。

コメント

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