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

Source: https://php-school.pages.dev/topics/session-cookie/ch06-session-security/
Site: AIと学ぶPHPの学校 (https://php-school.pages.dev)
Lesson: セッション・Cookie・ログイン
Level: L4

Reference (official): https://www.php.net/manual/ja/book.session.php

## 本文

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

## 概要

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 が **異なる** ことをコードで確認できる

## 所要時間

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

## ドリル

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

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

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

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

## スライド

---
marp: true
theme: dojo
paginate: true
size: 16:9
---

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

→ ドリルへ
