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

HTTPレスポンス分割攻撃:ヘッダーの「改行コード」が招く壊滅的なセキュリティ事故

現場でコードレビューをしていると、未だに「HTTPヘッダーへの動的な値の挿入」を軽視しているエンジニアに出くわす。Set-Cookieにユーザー入力をそのまま放り込むコードを見て、「まあ、英数字しか入らないだろうし」と楽観視していないか?

HTTPレスポンス分割(CRLFインジェクション)は、古典的だが極めて強力な攻撃だ。攻撃者はHTTPレスポンスのヘッダーに「CR(\r)LF(\n)」を注入することで、サーバーが生成するレスポンスの構造を意図的に破壊する。

1. なぜ「改行」が武器になるのか?(攻撃のメカニズム)

HTTPプロトコルにおいて、ヘッダーとボディの境界は \r\n\r\n というシーケンスで定義されている。攻撃者がこのCRLFを注入すると、サーバーはそれを「ヘッダーの終了」と誤認する。その結果、本来のヘッダーの後に、攻撃者が作成した偽のヘッダーや、悪意のあるJavaScriptコード(ボディ)を後続させることが可能になる。

攻撃のシナリオ:
1. 攻撃者が細工したURL(例:?redirect=http://evil.com/%0d%0aSet-Cookie:session=hacked)をユーザーに踏ませる。
2. サーバーがこれをそのまま Location ヘッダーに出力。
3. ブラウザは「改行」を認識し、偽のクッキー設定や、最悪の場合はクロスサイトスクリプティング(XSS)としてHTMLを解釈してしまう。

これは単なるリダイレクト汚染にとどまらない。キャッシュサーバーを汚染し、無実のユーザー全員に偽のコンテンツを配信させる「Webキャッシュ汚染」へと繋がる、極めて危険な脆弱性だ。

—

2. 【現場の鉄則】安全な実装サンプル

現代の言語フレームワークの多くは、ヘッダーへのCRLF注入を自動的に防ぐ(例外を投げる)ようになっている。しかし、レガシーな環境や生でHTTPレスポンスを制御するコードを書く際は、以下のルールを徹底してほしい。

PHPによるセキュアな実装例

PHPでは、ユーザー入力から改行コードを徹底的に除去することが鉄則だ。

Python (Flask) の場合

Flask等のモダンなフレームワークは、標準でヘッダーへのCRLF注入をチェックしているが、「値を直接連結しない」という基本を忘れてはならない。

from flask import redirect, request, abort
import re

@app.route(‘/redirect’)
def secure_redirect():
target = request.args.get(‘url’, ‘index.html’)

# そもそもヘッダーに改行を混ぜるロジックを避ける
# 信頼できない入力は常にバリデーションを通す
if not target.startswith(‘/’):
abort(400, “Invalid redirect path”)

return redirect(target)

—

3. インフラ層での防御:WAFとNginxの設定

アプリケーションコードの修正が即座にできない場合、あるいは多層防御(Defense in Depth)の観点から、フロントエンドで悪意あるリクエストを遮断する。

NginxでCRLFをブロックする

リクエストラインにCRLFが含まれる場合、Nginxで拒否する設定だ。

nginx.conf または 各サイトの設定ファイル
リクエストラインに制御文字が含まれることを防ぐ
ただし、アプリケーション側での処理が優先されるべきである
location / {
# 脆弱性を突くリクエストをログに記録し、400を返す
if ($request_uri ~ “(\r|\n)”) {
return 400;
}
proxy_pass http://backend_server;
}

WAFによる防御

AWS WAFやCloudflareなどのクラウドWAFを導入している場合、「CRLFインジェクション」を検知するマネージドルールを有効化するのが最も効率的だ。自前で正規表現を書くよりも、攻撃者のシグネチャを常時更新してくれるベンダーの知見を借りるのが、CISSP的なベストプラクティスである。

—

まとめ:セキュリティの「勘所」

HTTPヘッダーインジェクションを撲滅するためのポイントは、以下の3点に集約される。

1. 外部入力を信頼するな: ヘッダーにセットする値は、ホワイトリスト形式で検証するか、改行コードを「問答無用で削除」せよ。
2. フレームワークの恩恵を受けろ: header() 関数を直書きするような古いコードは、現代的なルータークラスやレスポンスオブジェクトに置き換える。
3. 境界線に注意せよ: HTTPレスポンスを生成する箇所は、アプリケーションの「出口」だ。ここを通過するすべてのデータに対して、汚染除去(Sanitization)を適用する習慣をチームで共有すること。

コードを書くとき、常に問いかけてほしい。「このデータが、HTTPの仕様をねじ曲げる可能性はないか?」と。その疑念こそが、インシデントを防ぐ最強のセキュリティツールになる。

コメント

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