こんにちは!Webアプリケーション開発の世界へようこそ。
新人のIT担当者や、これからセキュリティの勉強を始めるという方にとって、Webの仕組みは目に見えない部分が多くてちょっぴり不安になりますよね。「ちゃんと安全なサイトを作れているだろうか…」と心配になるのは、エンジニアとしてとても大切な第一歩です。
今回は、Webの裏側でこっそりやり取りされている「HTTPヘッダー」というお手紙のやり取りを悪用する、「HTTPヘッダーインジェクション」と「レスポンス分割攻撃」についてお話しします。
難しそうな名前ですが、身近な「家の鍵と泥棒のトリック」に例えながら、一歩ずつ分かりやすく紐解いていきましょう!
—
1. 家の鍵に例える「HTTPヘッダー」と「改行」の危険な関係
まずは、私たちが普段何気なく使っているWebブラウザと、サーバーのやり取りをイメージしてみましょう。
ブラウザ(あなた)とサーバー(お店の店員さん)が、伝言メモを使って会話をしていると想像してください。
サーバーは、あなたからの「このページを見せて」というメモに対して、以下のような「お返事のメモ(レスポンスヘッダー)」を書き込んで渡してくれます。
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Set-Cookie: session_id=abc123xyz
このお返事のメモには、「これは普通のページですよ(Content-Type)」とか、「あなた専用の合言葉(セッションID)はこれですよ(Set-Cookie)」という大事な情報が、1行ずつ綺麗に整理されて書かれています。
ここでポイントなのが、「行の変わり目(改行コード)」です。
店員さんは、「ここで1行終わり、次の行へ行くよ」という目印として、改行を使ってメモを綺麗に区切っています。
泥棒のトリック:改行を乗っ取り、勝手に手紙を書き換える
もし、このメモの書き込み欄に、悪意を持った人が「勝手に新しい行を作る魔法の文字(改行コード)」をこっそり混ぜ込むことができたらどうなるでしょうか?
泥棒は、ユーザーが入力するフォームなどに、次のような特別な文字を仕込みます。
(※URLエンコードと呼ばれる形式で、\r\n は改行を表します)
%0d%0aLocation: http://evil-site.com
これをそのままサーバーが受け取ってしまい、チェックをせずにそのままお返事のメモに書いてしまうと、メモはこんな風にぐちゃぐちゃに書き換わってしまいます。
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Set-Cookie: session_id=abc123xyz
Location: http://evil-site.com ← 泥棒が勝手に追加した行!
これがHTTPヘッダーインジェクションの正体です。
本来のお返事の途中に、泥棒が勝手に新しい行(ヘッダー)を差し込むことで、ブラウザを全く関係ない詐欺サイト(http://evil-site.com)へ強制的に誘導してしまう(これをさらに発展させたのがレスポンス分割攻撃やキャッシュ汚染です)というわけですね。
—
2. 実際にコードで見てみよう:危ないPHPプログラム
では、実際の開発現場で、なぜこれが起きるのかをPHPのコードを例に見てみましょう。
以下のコードは、ユーザーから受け取った言語設定(lang パラメーター)を、そのままお返事の「クッキー(Set-Cookie)」に保存しようとする、ちょっと危なっかしいプログラムです。
<?
// 【危険な実装例】
// ユーザーからの入力を検証せずに、そのままヘッダーに埋め込んでいます
$user_language = $_GET['lang'];
// クッキーに言語設定を保存するお返事ヘッダーを組み立てる
// ここで $user_language に改行コードが含まれていると…大変なことに!
header("Set-Cookie: user_lang=" . $user_language);
echo "言語設定を " . htmlspecialchars($user_language, ENT_QUOTES, 'UTF-8') . " に変更しました!";
?>
上記のコードでは、$_GET['lang'] の中身をチェックしていません。
もし攻撃者が ?lang=ja%0d%0aX-Fake-Header: hacked のようなURLにアクセスすると、サーバーは以下のようなお返事を作ってしまいます。
Set-Cookie: user_lang=ja
X-Fake-Header: hacked
このように、本来想定していないヘッダーを自由に操られてしまうため、セッション固定化攻撃(他人のセッションIDを無理やりユーザーに覚え込ませる)や、Webサイトのキャッシュサーバーを騙すキャッシュ汚染につながってしまいます。
—
3. 一歩ずつ対策を学んでいきましょう!安全な防犯対策
「うわ、怖い!どうすれば防げるんだろう…」と思いましたか?
安心してください。対策はとてもシンプルで明確です。
鉄則はたった一つ。
「ユーザーから受け取った入力値は、絶対にそのままヘッダーに出力しない(改行文字が含まれていないか厳しくチェックする)」ことです。
対策1:改行文字(\r や \n)の検出と拒否
PHPなどのモダンな言語では、ヘッダーに出力する値に改行文字が含まれている場合、処理を途中でストップさせるのが最も確実な防衛策です。
<?
// 【安全な実装例】
$user_language = $_GET['lang'];
// 1. 入力値の中に改行文字(\r や \n)が含まれていないかチェックする
if (preg_match("/[\r\n]/", $user_language)) {
// 改行が見つかった場合は、不正なアクセスとして処理を中断する
header("HTTP/1.1 400 Bad Request");
exit("不正な入力値が検出されました。");
}
// 2. 安全が確認された値のみ、ヘッダーに出力する
header("Set-Cookie: user_lang=" . $user_language);
echo "言語設定を安全に変更しました!";
?>
このように、プログラムの入り口で「おかしな文字(改行)が入っていないか」を警備員さんのようにしっかりチェックするだけで、HTTPヘッダーインジェクションの脅威を綺麗にシャットアウトすることができます。
—
まとめ
- HTTPヘッダーインジェクションとは?
入力値に隠された「改行コード」を悪用して、サーバーからのお返事メモを勝手に書き換え、ユーザーを別の場所に誘導したりする攻撃のこと。
- なぜ危ないの?
セッションの乗っ取りや、偽情報のキャッシュ汚染など、サイト全体の信頼に関わる大きなトラブルにつながるから。
- どうやって防ぐの?
ユーザーからの入力値に改行文字(\r や \n)が混ざっていないかを必ずチェックし、含まれている場合はリクエストを拒否する!
セキュリティの対策は、一度コツを掴んでしまえば「当たり前の習慣」になります。
「ユーザーの入力は信じるな(Don’t trust user input)」という合言葉を胸に、安全で頑丈なWebアプリケーションを一緒に作っていきましょう!
コメント