採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

CSRF とトークン

テクノロジ系 / session-cookie (session / cookie / login / CSRF)

CSRF とトークン

Lesson 13 / Chapter 5

CSRF って何

Cross-Site Request Forgery = 「他サイトを経由した、なりすましリクエスト送信攻撃

1. ユーザーが bank.example にログイン済 (Session Cookie あり)
2. 攻撃者の罠サイト evil.example をユーザーが踏む
3. evil.example の中に隠された <form action="bank.example/transfer">
4. JS が自動 submit → ブラウザは bank.example の Cookie を一緒に送る
5. bank.example 側は「正規のログインユーザーの操作」と誤認して送金実行

→ Cookie が 自動で送られる という仕組みを悪用する攻撃。

どう防ぐか — トークン方式

そのサーバーが自分で発行したフォームでなければ受け付けない」を実現する。

1. /form アクセス時、サーバーがランダム文字列を生成 → $_SESSION に保存
2. <input type="hidden" name="csrf_token" value="ABC123..."> を埋める
3. ユーザーが submit → サーバーは送信値と Session 内の値を比較
4. 一致したら処理続行、違ったら拒否

攻撃者は Session 内のトークン を知らないので、評価できない。

トークン生成 (PHP)

<?php
session_start();

// 32 バイト = 16 進 64 文字のランダム値
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));

echo '<form method="post" action="/submit">';
echo '<input type="hidden" name="csrf_token" value="'
   . htmlspecialchars($_SESSION['csrf_token']) . '">';
echo '<input name="message">';
echo '<button>送信</button>';
echo '</form>';
  • random_bytes(32)暗号論的に安全な乱数
  • bin2hex() で 16 進文字列 (64 文字) に変換
  • form の hidden に埋めて、submit 時にサーバーへ戻ってくる

トークン検証 (PHP)

<?php
session_start();

$sent  = $_POST['csrf_token'] ?? '';
$saved = $_SESSION['csrf_token'] ?? '';

// ✅ hash_equals で安全に比較する
if (!hash_equals($saved, $sent)) {
    http_response_code(403);
    echo "CSRF 検証失敗\n";
    exit;
}

// 検証成功 → 本来の処理に進む
echo "OK\n";

なぜ hash_equals() か:

  • =====比較に掛かる時間が値で変わる (タイミング攻撃の足掛かり)
  • hash_equals()長さに関わらず一定時間 で比較する設計

トークン使い切りの作法 (ベストプラクティス)

<?php
// 検証 OK だったら即破棄して使い回しを防ぐ
if (hash_equals($saved, $sent)) {
    unset($_SESSION['csrf_token']);  // 一度きり
    // 処理続行
}
  • 同じトークンを何度も受け付けると、リプレイ攻撃の足掛かりになる
  • 通常は 1 フォーム = 1 トークン、検証後は破棄
  • ユーザーが連投したい場合は、検証後に 新しいトークンを発行 して返す

採点用: 1 スクリプトで生成 → 検証を擬似化

<?php
// (採点用スタブ ...)

// === リクエスト 1: form を返す ===
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));

// === リクエスト 2: form 送信を受ける ===
parse_str(trim(fgets(STDIN) ?: ''), $_POST);

if (hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
    echo "OK\n";
} else {
    echo "NG\n";
}

ドリルでは stdin から「攻撃者から見た送信値」を渡し、合致するか合致しないかを試す。

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

✅ CSRF が「他サイト経由でなりすますリクエスト送信攻撃」と説明できる

random_bytes + bin2hex でトークンを生成できる

$_SESSION['csrf_token'] に保存して form の hidden に埋められる

hash_equals() で安全に比較できる

✅ 「使い切り」のお作法を知っている

→ ドリルへ

考えてみよう

既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。