utsuroi csrf labs Lab 03 / 5 — Practitioner
03

Token がセッションに紐付いていない

CSRF token is valid but not bound to the user session

Lab 02 の指摘を受けて、うつろい EC はToken の検証を必須化した。$_POST['csrf_token'] が無いと 403。一見正しい実装。

しかし、サーバ側の Token 管理に新しいバグがある:Token は生成されているが、特定のユーザのセッションに紐付いていない。サーバは「過去に発行した Token のいずれか」が来れば許可してしまう。
つまり攻撃者が自分のアカウントでログインして取得した Token を、被害者の攻撃に再利用できる

① 攻撃者として うつろい EC にログイン → 自分のフォームから Token を取得
② その Token を罠サイトの hidden field に埋め込む
③ 被害者がページを開くと、攻撃者の Token を使った CSRF が成立

  1. 下の「① 攻撃者のセッションで Token を取得」を押す → 攻撃者用 Token が生成される
  2. 取得した Token が「② 攻撃ペイロード」に自動的に埋め込まれる
  3. 「③ 攻撃を実行」を押す → 罠サイトが起動し、その Token で被害者の操作を試行
攻撃者の Token: (未取得)
shop.utsuroi.example.com / mypage
// PHP
function validate_csrf() {
    $sent = $_POST['csrf_token'] ?? '';
    if (!$sent) return false;
    
    // ★バグ: 全 token プールから一致を探すだけ。
    //   特定セッションに紐付いた token かは確認していない。
    $all_tokens = $redis->hgetall('csrf_tokens');
    return in_array($sent, $all_tokens, true);
}
② 攻撃ペイロード
<!-- 取得した Token を hidden field に埋め込む -->
<form action="https://shop.utsuroi.example.com/account/email" method="POST">
  <input name="email" value="attacker@evil.example">
  <input name="csrf_token" value="(まだ Token を取得していません)">
</form>
ヒント 1 — なぜこれが脆弱性なのか

正しい実装では Token は「セッション ID + 生成された値」のペアで管理されます。送られた Token がそのセッションのものでなければ拒否。本ラボでは Token プールから「いずれかと一致するか」だけを見ているので、攻撃者の自分用 Token も通ってしまう。

正解(ネタバレ)

「① 攻撃者の Token を取得」 →「③ 攻撃を実行」の順に押す。攻撃者のセッションで取得された Token が罠 HTML に埋め込まれ、被害者ブラウザがその Token と被害者の Cookie の両方を送って攻撃成立。