topic: session-cookie (session / cookie / login / CSRF)
「PHP の Web アプリはページが変わると 前のリクエストの値を覚えていない」。これを解決する 2 つの道具 — Cookie と Session — を扱うトピック。
このトピックで身につくこと
- 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';- 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 は次の方針で書く。
- Cookie 経路を切る —
ini_set('session.use_cookies', '0')とsession_id('TESTSESSIONID')で固定 ID の Session を CLI 上で擬似化 - 往復シナリオを 1 スクリプトで再現 — 「ログイン → マイページ表示」のような 2 リクエスト相当を 1 本の PHP で連続実行
$_COOKIEは stdin から組み立てる —$_GETをparse_strで組み立てたのと同じ要領- 本物の動作確認 は各 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'];進め方
- 各 chapter の
slide.mdを読む (4〜5 分) drill/の問題を順番に解く (ch01 は読むだけ)- 採点:
php scripts/grade.php topics/session-cookie/<chapter>/drill/<drill>/ - 余裕があれば
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 でもアクセスできる (シンボリックリンク)。