Shikata Ga Nai

Private? There is no such things.

CSRFトークンが「セッションCookie」と紐づいていない脆弱性:フレームワーク間の連携ミスによる防御崩壊

Hello there, ('ω')ノ

✅ 脆弱性の概要

CSRFトークンがCookieに依存する形式で運用されているにもかかわらず、 そのトークンがセッションを識別するためのCookie(例:session)とは別のCookie(例:csrfKey)に紐づいている場合、 セッションとトークンが一致しているかどうかの検証が行われず、攻撃者がトークンを偽装可能になることがあります。


📚 実際のリクエスト例

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Cookie: session=pSJYSScWKpmC60LpFOAHKixuFuM4uXWF; csrfKey=rZHCnSzEp8dbI6atzagGoSYyqJqTz5dv

csrf=RhV7yQDO0xcq9gLEah2WVbmuFqyOq7tY&email=wiener@normal-user.com

ここで問題となるのは:

  • sessionはセッション管理用のCookie
  • csrfKeyはCSRFトークン検証用のCookie → この2つが連携していない

🎯 脆弱な実装のパターン

  • フレームワークA:セッション用(例:Express, Flaskなど)
  • フレームワークB:CSRFトークン用(例:DjangoのCSRFミドルウェア、AngularのCSRF保護)

別々のCookieを使用し、照合処理も別々に動いている


🧠 攻撃者のバイパス方法

  1. 攻撃者が自身のセッションでログインし、csrfKeycsrfトークンの値を取得
  2. 被害者を攻撃用ページに誘導し、以下のようなHTMLを使ってCSRF攻撃を実行:
<form method="POST" action="https://vulnerable-website.com/email/change">
  <input type="hidden" name="email" value="attacker@evil-user.net">
  <input type="hidden" name="csrf" value="RhV7yQDO0xcq9gLEah2WVbmuFqyOq7tY">
</form>
<script>
  document.cookie = "csrfKey=rZHCnSzEp8dbI6atzagGoSYyqJqTz5dv";
  document.forms[0].submit();
</script>

→ 攻撃者のcsrfcsrfKeyが送信されるが、 セッションCookie(session)は被害者のものなので、 攻撃は「被害者の操作」として実行されてしまう


✅ 本来あるべき実装

実装内容 理由
CSRFトークンはセッション情報と1対1で関連づける 攻撃者が自分のトークンを他人に使わせることを防ぐ
複数のCookieを使う場合は相互照合を必須にする csrfcsrfKeyと一致するだけでは不十分
フレームワークのCSRF保護を統一する セッション管理とCSRF保護を別にしない

⚠️ よくある実装ミスの原因

原因 内容
複数のフレームワークを併用している バックエンドとフロントエンドで別のCSRF保護を使っている
CookieベースのCSRF保護に依存しすぎている JavaScriptで改ざん可能なため防御効果が限定的

✅ まとめ

CSRFトークンを「Cookieと一致すればOK」と判断する設計は非常に危険です。

  • 攻撃者は自分のcsrfKeycsrfのセットを送りつけることでバイパスが可能
  • 必ず「このトークンが現在のセッションのものであるか?」をサーバー側で検証すべきです

Best regards, (^^ゞ