はじめに:SessionIDを守り切ったはずの男
ユーザーのログイン状態を管理する「セッションID」。
これをCookieに保存する際、セキュリティ意識の高い(つもりの)私は、迷わず一つの属性を付与しました。
Set-Cookie: session_id=abc12345; HttpOnly;
「よし!これで悪意のあるJavaScript(XSS)を食らっても、document.cookie でセッションIDを盗み取られることはない。セッションハイジャックは完全に防げるぞ!」
数日後、私が管理するサイト上で、あるユーザーの掲示板アカウントから「怪しい投資用URL」が大量に自動スパム投稿される事件が起きました。(やばすぎて数分フリーズしたw)
私は混乱しました。
「なんでだ!?CookieはHttpOnlyにしてあるから、攻撃者はセッションIDを盗めないはずなのに!どうやってアイツに『なりすまし』たんだ!?」
技術解説:盗めなくても「操る」ことはできる(Session Riding)
HttpOnly 属性は、XSS対策として非常に強力かつ重要な設定です。これをつけることで、攻撃者はJavaScriptを使ってCookieの「中身を読み取る」ことができなくなります。つまり、あなたの自宅の鍵(セッションID)そのものを盗んで持ち去ることは不可能になります。
しかし、初心者の私は致命的な誤解をしていました。
XSSが存在する環境下では、攻撃者は「鍵を盗む」必要すらないのです。「あなたが鍵を開けた後の家に入り込み、あなたの代わりにボタンを押させる」ことができるからです。
恐怖の「リモートコントロール」
XSS(クロスサイトスクリプティング)によって、ユーザーのブラウザ上で不正なJavaScriptが実行されたとします。
攻撃者はセッションIDを盗めませんが、代わりにこのようなスクリプトを仕込みます。
// 【攻撃者のスクリプト】
fetch('/api/post_message', {
method: 'POST',
body: '変な投資のURL!登録してね!',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
このコードがユーザーのブラウザ上で実行された瞬間、何が起きるか。
ブラウザは親切にも、「あ、同じドメイン宛ての通信だな!じゃあHttpOnlyのCookie(鍵)も一緒にくっつけて送信しといたるわ!」と気を利かせます。
結果、バックエンドサーバーは「お、正規のセッションIDを持ったユーザー本人からの書き込みリクエストだな。ヨシ!」と判断し、スパム投稿を許可してしまうのです。これを Session Riding(セッションへの相乗り)と呼びます。
HttpOnlyは「致命傷を避ける防具」にすぎない
私は「HttpOnlyを付ければ、XSS攻撃を無効化できる」と本気で信じていました。
しかし現実は、「鍵自体は盗まれないから、明日以降に他のPCからログインされる完全な乗っ取り(クローン作成)は防げる。しかし、『今その瞬間』にあなたのフリをして好き勝手に操られることまでは防げない」という絶望のルールでした。
HttpOnly は素晴らしい防具ですが、それは「即死を避けるためのヘルメット」であり、飛んでくる矢(XSS)そのものを防ぐ盾ではありません。
最も重要なのは、大前提として**「XSSを絶対に許さない(エスケープの徹底)」**という当たり前の基礎だったのです。
プロパティを1つ叩いただけで「完全にセキュアだ」と錯覚してしまった私の慢心は、こうして見事に打ち砕かれました。