セッション固定攻撃:なぜ「ログイン前後」のID再生成がシステムの生死を分けるのか
現場で長年インシデント対応をしていると、「認証は通っているのに、なぜか権限昇格が起きている」という不可解な事案に遭遇することがあります。その黒幕の正体の一つが「セッション固定攻撃(Session Fixation)」です。
多くのエンジニアが「セッション管理はフレームワークがやってくれるから大丈夫」と高を括っていますが、フレームワークは「再生成のタイミング」までは制御してくれません。 ここでは、教科書的な説明は抜きにして、攻撃者がどうやって獲物を仕留めるのか、そして我々エンジニアがどうコードで防ぐべきか、泥臭い実務の視点から解説します。
—
1. 攻撃者が狙う「隙間」:PoCのメカニズム
セッション固定攻撃の目的はシンプルです。「攻撃者が用意したセッションIDを、被害者に使わせる」こと。
攻撃の流れはこうです。
1. 餌の設置: 攻撃者は攻撃対象のWebサイトにアクセスし、有効なセッションID(例: SESSID=evil12345)を取得します。
2. 罠の配布: 攻撃者は被害者に「ログインしてね」とURLを送ります(例: https://bank.example.com/?SESSID=evil12345)。もしURLパラメータでセッションIDを受け入れる仕様なら、被害者のブラウザにそのIDがセットされます。
3. ログイン待機: 被害者がそのIDを持ったままログインします。
4. 乗っ取り: サーバー側でセッションIDが再生成されなければ、サーバーは「evil12345=認証済みユーザー」と認識します。攻撃者は自分のブラウザで同じIDを使ってアクセスし、被害者のアカウントに入り込みます。
重要な盲点: 多くの開発者が「Cookieさえ盗まれなければ安全」と考えがちですが、「Cookieを盗む必要はない、最初から自分のを知っていればいい」というのがこの攻撃の恐ろしいところです。
—
2. 鉄則:ログイン時に「セッションを捨てて作り直す」
防御の基本は、「認証状態が変わる瞬間に、セッションIDを確実に破棄して再発行する」こと。これに尽きます。
PHPによる実装例
PHPでは session_regenerate_id(true) を呼ぶのが定石です。true を指定することで、古いセッションファイルを削除し、新しいIDに切り替えます。
<?php
// ログイン処理の直後、必ずこの関数を呼ぶ
function login_success_handler($user_id) {
// 1. 古いセッションを破棄し、新しいIDを発行する
// trueを指定することで、旧セッションファイルをサーバーから削除する
session_regenerate_id(true);
// 2. 認証セッションをセット
$_SESSION['user_id'] = $user_id;
$_SESSION['authenticated'] = true;
}
?>
—
3. Cookie属性を「武装」させる
セッションIDを再生成しても、Cookieの設定が甘ければ XSS などで簡単に盗まれます。以下の3つの属性は、設定ではなく「必須の守備隊」と考えてください。
php.ini での推奨設定
; HttpOnly: JavaScriptからのアクセスを禁止(XSS対策)
session.cookie_httponly = 1
; Secure: HTTPS経由でしかCookieを送信しない(盗聴対策)
session.cookie_secure = 1
; SameSite: CSRF対策。厳格にするならStrict、利便性重視でもLaxを明示
session.cookie_samesite = "Lax"
Nginxでヘッダーを強制上書きする場合
もしPHP側での制御が不安なら、Webサーバー側で強制的にヘッダーを付与するのも賢い戦略です。
# nginx.conf の server または location ブロック
add_header Set-Cookie "Path=/; HttpOnly; Secure; SameSite=Lax";
—
4. 現場の教訓:なぜ「フレームワーク依存」が危険なのか
よくある失敗は、ログイン用のAPIを独自に作ったり、古いコードベースをそのまま使い続けたりすることです。特に「URLパラメータでセッションIDを受け入れる仕様(PHPSESSID=xxxをURLに含める)」が有効になっていると、上記の防御コードを書いても意味がありません。
チェックリスト:
php.iniのsession.use_trans_sidが0になっているか?(URLにIDを埋め込む機能を無効化)session.use_only_cookiesが1になっているか?(Cookie以外でIDを受け付けない)- ログイン時に
session_regenerate_id(true)を呼び忘れていないか?
—
最後に:セキュリティは「性悪説」で設計する
我々が守るべきは「信頼できるユーザー」ではなく、「常に攻撃手法をアップデートしている攻撃者」です。
「とりあえず動く」コードを書くのはジュニアエンジニアの仕事です。シニアエンジニアは、「セッションが奪われたらどうなるか? 認証が突破されたらどうなるか?」という最悪のシナリオを想像し、そのプロセスを遮断するコードを書きます。
セッション固定攻撃は、ログイン処理のわずか1行の記述漏れから始まります。今日からすぐに、自社コードのログインハンドラを確認してください。「再生成」というたった一つのアクションが、数百万件の個人情報流出を未然に防ぐ防波堤になるのです。
コメント