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

「PHP の Web アプリはページが変わると 前のリクエストの値を覚えていない」。これを解決する 2 つの道具 — CookieSession — を扱うトピック。

このトピックで身につくこと

  • Cookie と Session の役割の違いを 1 行で言える
  • session_start() + $_SESSION でログイン状態を保てる
  • setcookie() + $_COOKIE で軽い情報をブラウザに覚えさせられる
  • CSRF トークンを Session 経由で form に埋め、サーバー側で検証できる
  • session_regenerate_id(true) でセッション固定化攻撃を防げる

前提知識の要点

$_GET / $_POST の扱い (topic: web) が前提。連想配列・スーパーグローバル・stdin スタブの考え方をそのまま再利用する。

<?php
session_start();
$_SESSION['user'] = '太郎';     // サーバー側に保存
setcookie('theme', 'dark');     // ブラウザに保存

echo $_SESSION['user'] ?? 'guest';
echo $_COOKIE['theme']  ?? 'light';
▶ 3v4l で実行
  • Cookie = ブラウザに保存される小さな key/value。毎リクエストで自動送信される。ユーザーが改ざんできる
  • Session = サーバー側に保存される連想配列。改ざんされない。Cookie の中の Session ID で「どのユーザーの session か」を識別する

「秘密にしたい値は Session、ブラウザに覚えさせる軽い情報は Cookie」と覚える。

変数 何が入っているか 保存場所
$_GET / $_POST URL / form の値 リクエストごと
$_COOKIE ブラウザが送ってきた Cookie ブラウザ
$_SESSION サーバー側の連想配列 サーバー (Cookie の Session ID で紐付く)

chapter 一覧

# chapter 内容
1 ch01-what-is-session/ Cookie / Session / Stateless HTTP (ドリルなし)
2 ch02-session-basic/ session_start()$_SESSION
3 ch03-cookie-basic/ setcookie()$_COOKIE
4 ch04-login-flow/ ログイン状態を $_SESSION で保つ
5 ch05-csrf/ CSRF とトークン (hash_equals)
6 ch06-session-security/ session_regenerate_id(true) / 固定化攻撃対策

合計 6 chapter / 10 drill / 所要 3〜3.5 時間

採点と「本物のセッション動作」の二段構え

session_start() / setcookie() は本来 Web サーバー経由でしか正しく動かない。CLI で実行するとヘッダ警告が出たり Cookie が永続化されなかったりする。

そこで drill は次の方針で書く。

  1. Cookie 経路を切るini_set('session.use_cookies', '0')session_id('TESTSESSIONID') で固定 ID の Session を CLI 上で擬似化
  2. 往復シナリオを 1 スクリプトで再現 — 「ログイン → マイページ表示」のような 2 リクエスト相当を 1 本の PHP で連続実行
  3. $_COOKIE は stdin から組み立てる$_GETparse_str で組み立てたのと同じ要領
  4. 本物の動作確認 は各 chapter README で php -S localhost:8000 を案内する
<?php
// 採点用: header/cookie 警告を抑止して Session を擬似化
ini_set('session.use_cookies', '0');
ini_set('session.use_only_cookies', '0');
session_id('TESTSESSIONID');
@session_start();

// 以降は本物の Web アプリと同じ
$_SESSION['name'] = '太郎';
echo $_SESSION['name'];
▶ 3v4l で実行

進め方

  1. 各 chapter の slide.md を読む (4〜5 分)
  2. drill/ の問題を順番に解く (ch01 は読むだけ)
  3. 採点: php scripts/grade.php topics/session-cookie/<chapter>/drill/<drill>/
  4. 余裕があれば php -S localhost:8000 でブラウザ起動を試す

つまづきポイント

症状 多くの原因
Cannot start session 警告 CLI 実行で session.use_cookies=0 を入れ忘れ
$_SESSION が空 session_start() を呼んでない / 採点スタブで session_id を固定してない
Cookie がブラウザに残らない setcookie()echo に呼んでいる (ヘッダは本文より前)
CSRF トークン検証が常に NG == で型違い比較 / hash_equals() を使ってない
session_regenerate_id() で新旧 ID が同じ true (古い session を破棄するフラグ) を渡し忘れ

関連トピック

トピック 関係
web $_GET / $_POST の延長。前提として必要
array-assoc $_SESSION / $_COOKIE は連想配列
db ユーザー情報を DB に持たせ、Session ID で参照
file-io ログ出力 / 設定ファイル読み込み

トピックを並列で参照する全体地図は TOPICS_INDEX.md にある。

案件 (dojo_map.tsv) での参照

topic_slug       chapter_dir
session-cookie   topics/session-cookie/ch02-session-basic
session-cookie   topics/session-cookie/ch04-login-flow
session-cookie   topics/session-cookie/ch05-csrf
session-cookie   topics/session-cookie/ch06-session-security

slug session-cookie で参照可。13-session-cookie / session-cookie どちらの path でもアクセスできる (シンボリックリンク)。

このレッスンの章

  1. ch01 ch01 — Cookie / Session / Stateless HTTP の限界
  2. ch02 ch02 — `session_start()` と `$_SESSION` の基本
  3. ch03 ch03 — `setcookie()` と `$_COOKIE`
  4. ch04 ch04 — ログイン状態を `$_SESSION` で保つ
  5. ch05 ch05 — CSRF とトークン
  6. ch06 ch06 — Session セキュリティ (regenerate_id / 固定化)