topic: session-cookie (session / cookie / login / CSRF) / ch06

ch06 — Session セキュリティ (regenerate_id / 固定化)

学習目標

  • Session Fixation (固定化攻撃) が何を狙うかを 1 行で言える
  • ログイン時に session_regenerate_id(true) を呼んで Session ID を作り直す 意味を説明できる
  • 新旧 Session ID が 異なる ことをコードで確認できる

所要時間

スライド 5 分 + ドリル 2 問 = 25 分

ドリル

no 内容
01 ログイン時に session_regenerate_id(true) を呼び、新旧 ID が違うことを "changed: yes" の形で出力
02 固定化攻撃のシナリオを擬似化 — 既知の ID で開始 → ログイン → ID が変わったか検証

本物のセッション動作で確認したい場合

cd topics/13-session-cookie/ch06-session-security/drill/01-regenerate-id/
php -S localhost:8000 answer.php

ブラウザの開発者ツール (Application → Cookies → PHPSESSID) を見ながらリロードし、ログイン処理を呼んだ瞬間に Session ID が変わることを確認。

解説

この章の本体解説です(教材スライド由来)。

<!-- LLM_CONTEXT: Lesson 13 / Chapter 6 目的: Session Fixation の概念 / session_regenerate_id でログイン時に ID を作り直す 扱わない: CSRF (ch05) / Cookie 基礎 (ch03) 読み上げ時間目安: 4 分半〜5 分 -->

Session セキュリティ — Regenerate ID

Lesson 13 / Chapter 6


Session Fixation 攻撃って何

「攻撃者が 既に知っている Session ID をユーザーに使わせて、ログイン後の Session を乗っ取る」

1. 攻撃者がサイトにアクセス → サーバーから Session ID="ABC123" を取得
2. 攻撃者が罠リンクで ABC123 をユーザーに使わせる
   例: https://target.example/?PHPSESSID=ABC123
3. ユーザーが ABC123 のまま target.example にログイン
4. サーバー: 「ABC123 さん、ログイン完了」
5. 攻撃者: ABC123 を自分のブラウザに設定 → ログイン状態を共有して乗っ取り

ログイン前後で Session ID が同じ ことが弱点。


防御策: session_regenerate_id(true)

<?php
session_start();

$oldId = session_id();   // ログイン前の ID
session_regenerate_id(true);   // 新 ID を発行 + 古い Session を破棄
$newId = session_id();   // ログイン後の ID

// $oldId !== $newId が保証される
$_SESSION['user'] = ['name' => '太郎'];
▶ 3v4l で実行
引数 意味
true 古い Session を破棄 (必須に近い)
false (デフォルト) 古い Session は残ったまま (危険)

必ず true を渡す。これを忘れると、固定化された ID も生き続けてしまう。


いつ呼ぶか

タイミング 理由
ログイン直後 ログイン前の ID で固定化されてもログイン後 ID で無効化される
権限昇格時 (一般 → 管理者) 同じ理由。重要操作の前に再生成
ログアウト時 session_destroy() するので不要だが、念のため再生成しても良い

何らかの権限が変わる瞬間に ID を作り直す」のが原則。


採点用: ID 変更を確認

<?php
// (採点用スタブ ... session の擬似化)

$oldId = session_id();       // 採点用は固定 ID "TESTSESSIONID"
session_regenerate_id(true);
$newId = session_id();

if ($oldId !== $newId) {
    echo "changed: yes\n";
} else {
    echo "changed: no\n";
}
▶ 3v4l で実行

CLI でも session_regenerate_id() は新しい ID を生成してくれる。比較で「変わったか」を検証する。


他の防御策 (概念だけ)

対策 効果
session.cookie_httponly=1 JS から PHPSESSID Cookie を読めなくする (XSS 経由の盗難を防ぐ)
session.cookie_secure=1 HTTPS 通信でしか PHPSESSID を送らない
session.cookie_samesite=Lax 他サイトからのリクエストに PHPSESSID を載せない (CSRF と二重防御)
session.use_strict_mode=1 サーバーが発行していない ID を拒否

php.ini または ini_set() で設定。全部 ON にしておけば固定化攻撃にも強くなる


このチャプターでできるようになること

✅ Session Fixation が「事前に知った ID をユーザーに使わせて乗っ取る攻撃」と言える ✅ session_regenerate_id(true) を呼ぶ意味と引数の役割を説明できる ✅ ログイン時に必ず ID を再生成するコードを書ける ✅ 他の防御策 (httponly / secure / samesite / strict_mode) を 1 行で挙げられる

→ ドリルへ

演習問題の詳細

この章の演習問題の内容を読めます。実際に手元で解くには教材リポジトリを clone してください。

ドリル 01 — `session_regenerate_id(true)` で ID を作り直す

問題

ログイン時の Session ID 再生成を再現してください。

  • session_start() 直後の Session ID (採点用スタブで TESTSESSIONID) を変数に保存
  • session_regenerate_id(true) を呼ぶ
  • 再取得した Session ID と比較し、異なれば "changed: yes"、同じなら "changed: no" を 1 行出力

期待される出力:

changed: yes

採点

php scripts/grade.php topics/13-session-cookie/ch06-session-security/drill/01-regenerate-id/

ヒント

$oldId = session_id();
@session_regenerate_id(true);
$newId = session_id();

echo ($oldId !== $newId ? "changed: yes" : "changed: no") . "\n";
▶ 3v4l で実行

本物のセッション動作で確認したい場合

cd topics/13-session-cookie/ch06-session-security/drill/01-regenerate-id/
php -S localhost:8000 answer.php

ブラウザの開発者ツールで PHPSESSID を見ながらリロード、再生成のタイミングで Cookie 値が変わることを確認。

ドリル 02 — Session Fixation シナリオ — ログイン時に ID 再生成で防ぐ

問題

固定化攻撃のシナリオを 1 スクリプトで擬似化します。

シナリオ:

  1. 攻撃者から渡された ID ATTACKERFIXEDID で Session を開始 (採点用スタブで session_id() をこの値にセット)
  2. ユーザーがログイン処理を実行
  3. ログイン直後に session_regenerate_id(true) を呼ぶ
  4. ログイン後の Session ID が攻撃者の渡した固定 ID と 異なる ことを確認

出力仕様:

  • 「ログイン前の ID」と「ログイン後の ID」が同じか異なるかを判定
  • 異なれば "safe: regenerated"、同じなら "vulnerable: fixation" を 1 行出力

期待される出力:

safe: regenerated

(session_regenerate_id(true) を呼ばないと vulnerable: fixation になる)

採点

php scripts/grade.php topics/13-session-cookie/ch06-session-security/drill/02-session-fixation/

ヒント

// スタブで session_id('ATTACKERFIXEDID') がセットされている前提
$oldId = session_id();                    // ATTACKERFIXEDID
$_SESSION['user'] = ['name' => '太郎'];   // ログイン
@session_regenerate_id(true);             // ★ ここがポイント
$newId = session_id();

echo ($oldId !== $newId ? "safe: regenerated" : "vulnerable: fixation") . "\n";
▶ 3v4l で実行

ポイント

  • ログイン直後に必ず session_regenerate_id(true) を呼ぶ のが固定化攻撃の最小防御
  • true を渡し忘れると古い Session が残るので注意 (引数の名前は delete_old_session)
  • ログアウト時の session_destroy() と組み合わせて運用するのが定石

演習問題(2問)

  1. ドリル 01 — `session_regenerate_id(true)` で ID を作り直す

    README.md starter.php answer.php

  2. ドリル 02 — Session Fixation シナリオ — ログイン時に ID 再生成で防ぐ

    README.md starter.php answer.php

サイト内で問題文・雛形・解答例を確認できます。実際に手元で解くには教材リポジトリを clone してください。