セッション固定攻撃:ログイン後の「ID使い回し」が招く悲劇とその防衛術
やあ。現場でコードを書き、時にはインフラの穴を突く我々にとって、セッション管理は「Webアプリの心臓部」だ。だが、この心臓部に致命的な不整脈を起こさせるのが「セッション固定攻撃(Session Fixation)」だ。
教科書には「攻撃者が用意したIDを被害者に使わせる」とあるが、現場の感覚で言えば、これは「鍵を渡した覚えがないのに、相手が合鍵を持って部屋に入ってくる」という、極めて不気味な侵入だ。今回は、この攻撃の本質と、明日からお前のプロジェクトで「絶対に」実装すべき防御策を、泥臭い知見を交えて解説する。
—
1. セッション固定攻撃のメカニズム:なぜ「固定」が脅威なのか
セッション固定の恐ろしい点は、攻撃者が被害者の認証プロセスに一切関与せず、単に「既知のセッションID」を被害者に踏ませるだけで成立する点だ。
攻撃のフロー
1. 準備: 攻撃者が自ら対象サイトにアクセスし、有効なセッションID(例: PHPSESSID=attacker_fixed_id)を取得する。
2. 誘導: 攻撃者はURLパラメータやXSS、あるいは悪意あるリンクを通じて、被害者にこのIDを送り込む。
3. 強制: 被害者がそのリンクをクリックすると、ブラウザはそのID(attacker_fixed_id)をセットした状態でサイトにアクセスする。
4. 乗っ取り: 被害者がその状態でログインを行う。もしシステムが「ログイン前後でIDを更新しない」設計であれば、サーバー側は認証後のセッションを、攻撃者が知っているIDと結びつけてしまう。
5. 完了: 攻撃者は自分のブラウザでattacker_fixed_idを使って再アクセスするだけで、被害者の権限で認証済み状態になっている。
—
2. 現場で使える防御の絶対ルール
「ログイン時にセッションIDを再生成する」。これに尽きる。ログインという「権限が昇格するタイミング」で、それまでの無効なIDを捨て、新しいIDを発行すること。これが唯一にして最強の防御だ。
PHPでのセキュアな実装例
PHPで最もやってはいけないのが、ログイン処理時にsession_id()を直接触ることだ。必ず以下の関数を使うこと。
<?php
// ログイン成功時に必ず実行する
function secure_login($user_id) {
// 1. セッションIDを新しく生成し、古いセッションデータは破棄する
// trueを渡すことで、古いセッションファイルを削除してくれる
session_regenerate_id(true);
// 2. 認証済みフラグとユーザーIDを保存
$_SESSION['authenticated'] = true;
$_SESSION['user_id'] = $user_id;
// 3. 次回アクセス以降に備えて、セッションの有効期限を明示的に更新することも検討せよ
}
?>
Python (Flask) での実装
Flaskでも同様に、ログイン処理の直後に session.clear() や session.regenerate() に相当する処理を挟む必要がある。
from flask import session
@app.route('/login', methods=['POST'])
def login():
# 認証ロジック...
if user_is_valid:
# セッションの初期化とIDの再生成
session.clear()
session['user_id'] = user_id
return "Logged in"
—
3. Cookie属性:ブラウザを「守護神」にする
コードレベルの対策に加え、Cookieの設定で攻撃の成功率を極限まで下げる。これらはNginxやWebフレームワークの設定で必ず適用せよ。
Cookie設定のベストプラクティス(php.ini や設定ファイル)
; セッションIDをURLに含めない(必須)
session.use_only_cookies = 1
; クライアント側のスクリプトからIDを盗ませない
session.cookie_httponly = 1
; HTTPS経由のみで送信する
session.cookie_secure = 1
; CSRF対策と併せて、サードパーティへの送信をブロックする
session.cookie_samesite = "Lax"
Nginxでのヘッダー強制付与(WAF代わりの防御)
アプリケーションの変更が間に合わない場合でも、Nginx側で強制的にセキュアなCookie属性を付与できる。
# 全てのレスポンスに対してセッションCookieをセキュアにする設定例
proxy_cookie_path / "/; HTTPOnly; Secure; SameSite=Lax";
—
4. セキュリティチーフからの「現場の心得」
最後に、一つだけ覚えて帰ってほしい。「脆弱性は、技術ではなく『仕様の隙間』に潜む」ということだ。
- ログイン完了メッセージの罠: 「ログイン完了しました」というリダイレクト先のURLに、URLパラメータでセッションIDが含まれていないか? ログに残れば即アウトだ。
- 権限変更時の処理: ログイン時だけでなく、一般会員から管理者へ権限昇格するような「重要なステータス変更」の際も、セッションIDは再生成すべきだ。
- 監視を怠るな: 異常な頻度でセッションIDが切り替わっているか、あるいは同一IDで全く異なるIPアドレスからのアクセスが頻発していないか。SIEMやアクセスログでこの「不整合」を検知できるようにしておくこと。
コードは書くことよりも、どう「壊れないか」「壊させないか」を想像することに価値がある。お前が書くその一行が、ユーザーの財産を守る盾になることを忘れるな。何かあればいつでも相談してくれ。
コメント