ch06 6章中 6章目

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

スライドで見る

この章を AI と学ぶ

💬 AI に聞く質問例 3 件
  1. 「ch06 — Session セキュリティ (regenerate_id / 固定化)」のつまずきやすいポイントを、初学者向けに3つ教えてください

    ChatGPT Claude Gemini

  2. 「ch06 — Session セキュリティ (regenerate_id / 固定化)」について、自分の手で試せる練習問題を3つ出してください (難易度を上げながら)

    ChatGPT Claude Gemini

  3. 「ch06 — Session セキュリティ (regenerate_id / 固定化)」を学んだ後、次に学ぶと良いトピックは何ですか?理由も教えてください

    ChatGPT Claude Gemini

概要

Session の脅威のひとつが Session Hijacking (セッション乗っ取り)PHPSESSID を盗まれると、攻撃者は自分のブラウザに Cookie をセットするだけで「ログイン済みの被害者」になりすませる。特に怖いのが Session Fixation (固定化攻撃) で、攻撃者が事前に取得した Session ID を細工した URL 等で 被害者に使わせて、ログイン後の Session を共有してしまう手口。

これを防ぐ標準対策が session_regenerate_id(true)。ログイン成功の 直後に必ず呼ぶ ことで、新しい Session ID を発行して古い ID を破棄する (true が古い Session ファイルを消す指定)。事前に使わせた ID はログインの瞬間に無効化され、盗んでも意味がなくなる。ドリルでは regenerate 前後の ID を比較して目視確認する。

上乗せ防御として、Session Cookie には Secure 属性 (HTTPS でしか送らない) と HttpOnly 属性 (JavaScript から読めない) を必ず付ける。session.cookie_secure = 1 / session.cookie_httponly = 1 を php.ini か ini_set で設定するのが定石。本番運用なら 「regenerate_id + Secure + HttpOnly」の 3 点セット が常識ライン。

学習目標

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

ドリル

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

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

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

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

解説スライド 全 7 枚

右の ボタン キー(またはスワイプ)で次のスライドに進めます。 下の「縦表示」を開くと全スライドを一気に読めます。

  1. Session セキュリティ — Regenerate ID

    Lesson 13 / Chapter 6

  2. 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 が同じ ことが弱点。

  3. 防御策: 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' => '太郎'];
    引数 意味
    true 古い Session を破棄 (必須に近い)
    false (デフォルト) 古い Session は残ったまま (危険)

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

  4. いつ呼ぶか

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

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

  5. 採点用: 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";
    }

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

  6. 他の防御策 (概念だけ)

    対策 効果
    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 にしておけば固定化攻撃にも強くなる

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

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

    → ドリルへ

1 / 7
📜 スライドを全部一気に読む(縦表示)

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' => '太郎'];
引数 意味
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";
}

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 行で挙げられる

→ ドリルへ

この章で取り組む 演習問題の流れ (2ステップ)
  1. 01 ドリル 01 — `session_regenerate_id(true)` で ID を作り直す README.md starter.php answer.php
  2. 02 ドリル 02 — Session Fixation シナリオ — ログイン時に ID 再生成で防ぐ README.md starter.php answer.php

演習問題の詳細

この章の演習問題の内容を読めます。実際に手元で解くには教材リポジトリを 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

ヒント

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

echo ($oldId !== $newId ? "changed: yes" : "changed: no") . "\n";

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

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

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

🧪 ブラウザで試す

スライドのコードを写経して ▶ 実行、自分で書いて 📝 採点 で答え合わせ。初回のみ PHP ランタイム読込で数秒かかります。

PHP ランタイム未読込

          

        

ドリル 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 になる)

ヒント

// スタブで 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";

ポイント

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

🧪 ブラウザで試す

スライドのコードを写経して ▶ 実行、自分で書いて 📝 採点 で答え合わせ。初回のみ PHP ランタイム読込で数秒かかります。

PHP ランタイム未読込

          

        

演習問題(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 してください。

完了にすると進捗に反映されます