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の仕様をねじ曲げる可能性はないか?」と。その疑念こそが、インシデントを防ぐ最強のセキュリティツールになる。
コメント