セッション固定化攻撃:なぜ「ログイン前後でIDを変える」のが絶対の掟なのか
現場でコードを読んでいると、たまに「なぜログイン処理のたびにセッションIDを再発行するような面倒な処理が必要なのか?」と聞かれることがある。
結論から言おう。それをサボると、攻撃者はあなたのWebアプリの「玄関の鍵」を、マスターキーのようにあらかじめ準備できてしまうからだ。今日は、ペネトレーションテストの現場で今もなお遭遇する「セッション固定化(Session Fixation)」の残酷な現実と、それを一撃で無効化する防御策について、エンジニアの視点で深掘りしていく。
—
1. そもそもセッション固定化とは何か?
セッション固定化攻撃の核心は、「ユーザーがログインする前に、攻撃者が用意したセッションIDをユーザーのブラウザに強制的にセットさせる」という点にある。
通常のWebアプリは、ログインに成功するとサーバー側で「このブラウザは認証済みだ」とフラグを立て、そのセッションIDを「通行手形」として扱う。もし、ログイン前後でこの通行手形(セッションID)が変わらなければどうなるか?
攻撃者は、自分が発行させたセッションIDで犠牲者がログインするのを待ち構え、そのIDを使って裏からこっそりログイン済みセッションを乗っ取ることができる。パスワードを知る必要すらなく、ただ犠牲者を「釣り」にかけるだけでいい。
攻撃のフロー
1. 事前準備: 攻撃者がWebサイトにアクセスし、有効なセッションID(例: abc-123-xyz)を入手する。
2. 罠の設置: 攻撃者はこのIDを、何らかの方法(URLパラメータ、クロスサイトスクリプティングなど)で犠牲者のブラウザに送り込み、セットさせる。
3. 待ち伏せ: 犠牲者がそのIDを持った状態でログインを行う。
4. 乗っ取り: サーバー側でログインが完了するが、セッションIDは abc-123-xyz のまま。攻撃者は自分のブラウザでこのIDを使い、犠牲者のアカウントとして自由に振る舞う。
—
2. 実務で通用する「絶対防御」のコード実装
この攻撃を防ぐための鉄則はただ一つ。「認証の境界線で、必ずセッションIDを再発行せよ」だ。ログイン処理が成功した瞬間に、古いセッションを破棄し、新しいIDを払い出す。これだけで攻撃者は完全に排除される。
PHPでの実装例
PHPでセッションを扱う場合、session_regenerate_id(true) を呼ぶのが定石だ。ここで true を指定することで、古いセッションファイルを削除し、乗っ取りの芽を完全に摘む。
<?php
// ログイン認証成功後の処理
function handleLogin($username, $password) {
if (verifyUser($username, $password)) {
// 重要:セッションIDを再生成して古いものを破棄する
// これにより、ログイン前に固定されていたIDは無効化される
session_regenerate_id(true);
// ログイン状態を保存
$_SESSION['user_id'] = $username;
$_SESSION['authenticated'] = true;
return true;
}
return false;
}
?>
Python (Flask) での実装例
Flaskなどのフレームワークでも考え方は同じだ。セッションの書き換えを確実に行う。
from flask import session, session_regenerate_id # 概念的な例
@app.route('/login', methods=['POST'])
def login():
if verify_user(request.form['username'], request.form['password']):
# 既存のセッションデータを退避
old_session = dict(session)
# セッションをクリアして新しいIDを発行
session.clear()
session.update(old_session)
session['user_id'] = request.form['username']
return "Login Success"
—
3. インフラ・設定レベルでの「多層防御」
アプリコードを直すのが基本だが、設定レベルでも「セッションを盗ませない」ための防壁を築く必要がある。
Nginx/WebサーバーでのCookie属性設定
Cookieには HttpOnly と Secure、そして SameSite 属性を必ず付与すること。特に HttpOnly は重要だ。これが付与されていないと、万が一XSS脆弱性があった場合に、攻撃者が document.cookie でセッションIDを簡単に盗み出せてしまう。
# Nginxの設定例 (FastCGI等でPHPを動かす場合)
# Cookieに属性を付与して堅牢性を高める
fastcgi_param PHP_VALUE "
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = Strict
";
なぜこれが最強なのか
HttpOnly: JavaScriptからのセッションIDへのアクセスを遮断する。これでXSSによるID窃取を防ぐ。Secure: HTTPS通信時のみCookieを送信する。通信経路での盗聴を防ぐ。SameSite=Strict: CSRF対策の基礎。他サイトからのリクエスト時にCookieを送らなくなる。
—
チームへのメッセージ
「まあ、うちはそんなに攻撃されないから」という慢心が、最も大きなインシデントを生む。セッション管理はWebセキュリティの心臓部だ。もし、あなたの担当しているプロジェクトでログイン処理を確認して、session_regenerate_id が見当たらなかったら、それは「即座に改修すべき技術的負債」だと認識してほしい。
セキュリティは、派手なハッキング手法を防ぐことではなく、こうした「当たり前の実装」をいかに徹底できるかという、地味な積み重ねの先にある。次のリリースでは、必ずこのコードが反映されていることを期待しているよ。
コメント