CSRFの仕組みまとめ|成立条件と対策の基本

※本記事にはプロモーションが含まれています。

CSRFは「なんとなく危険そう」と理解されがちな脆弱性ですが、仕組みを分解するとシンプルです。

この記事では、CSRFを構成する要素を分けて、なぜ成立するのかを分解して整理していきます。

CSRFとは何か

CSRF(Cross-Site Request Forgery)は、ユーザーの意思とは関係なくリクエストを送らせる攻撃です。

重要なのは攻撃者が直接攻撃するのではなく、被害者のブラウザを使うという点です。


CSRFは被害者のブラウザを利用する攻撃

CSRFのポイントは次の一文に集約できます。

「被害者のブラウザにリクエストを送らせる」

攻撃者自身はログインしていなくても、ログイン済みのユーザーのブラウザを利用することで、正規ユーザーとして処理させることができます。


なぜCSRFが成立するのか

ここがCSRF理解の核心です。

Cookieは自動で送信される

ブラウザは、対象サイトに対するリクエストに対して、保存されているCookieを自動で付与します。

つまり、

  • ユーザーがログイン済み
  • Cookieがブラウザにある

この状態でリクエストが送られると、サーバーはそれを正規ユーザーの操作と判断します。

リクエストはどこからでも送れる

ブラウザは、他サイトからのリクエスト送信も許可します。

例えば以下のようなHTMLでもリクエストは送信されます。

<form action="https://example.com/email/change" method="POST">
    <input type="hidden" name="email" value="attacker@example.com">
</form>
<script>
    document.forms[0].submit();
</script>

ユーザーがこのページを開くだけで、対象サイトにリクエストが送信されます。


CSRFはどのような場面で発生するのか

CSRFは「ユーザーが怪しいリンクをクリックする」といった状況だけで発生するわけではありません。

実際には、ユーザーが通常のサイト閲覧をしているだけでも成立する場合があります。

リンクをクリックした場合

攻撃者が用意したURLをクリックすることで、リクエストが送信されるケースです。

  • メールに記載されたリンク
  • SNSの投稿
  • コメント欄のURL

このパターンは分かりやすいものの、CSRFの一部に過ぎません。

ページを開いただけで発生する場合

ページ内に埋め込まれた要素によって、ユーザーの操作なしでリクエストが送信されるケースです。

<img src="https://example.com/email/change?email=attacker@example.com">

画像の読み込み時にリクエストが送信されるため、ユーザーは何も操作していなくても攻撃が成立します。

自動送信フォームによる攻撃

JavaScriptによってフォームが自動送信されるケースです。

<form action="https://example.com/email/change" method="POST">
    <input type="hidden" name="email" value="attacker@example.com">
</form>
<script>
    document.forms[0].submit();
</script>

ページを開いた瞬間にリクエストが送信されます。

埋め込みコンテンツによる発生

iframeや広告など、外部コンテンツを通じてリクエストが発生するケースもあります。

  • iframeによる埋め込み
  • 広告スクリプト
  • 外部ウィジェット

ユーザーは攻撃に気づかないまま処理が実行されます。

CSRFはブラウザがリクエストを送る状況が発生すれば成立します。


CSRFが成立する条件とは

CSRFは次の条件が揃うと成立します。

  • ユーザーがログイン済みである
  • Cookieによる認証が使われている
  • 外部からリクエストを送信できる
  • サーバー側でリクエストの正当性を検証していない

この中で最も重要なのが最後の項目です。


サーバーがリクエストを信じてしまう理由

サーバー側は通常、次の情報しか見ていません。

  • Cookie(ログイン状態)
  • リクエスト内容

しかし、これだけでは

  • 本人の操作なのか
  • 攻撃によるものなのか

を区別できません。


CSRFトークンの役割

そこで使われるのがCSRFトークンです。

トークンは、正規ユーザーの画面から送信されたことを証明する値です。

CSRFトークンが付与されたときの例
▲ CSRFトークンが付与されたときの例

トークンの仕組み

  1. サーバーがランダムな値を生成
  2. フォームに埋め込む
  3. リクエスト時に一緒に送信させる
  4. サーバー側で一致を確認

攻撃者はこのトークンを取得できないため、正しいリクエストを再現できません。


CSRF対策の落とし穴|トークンがあっても防げないケース

ここが重要です。

CSRF対策は「トークンがある」だけでは不十分です。

よくあるミス

if (isset($_POST['csrf'])) {
validate_csrf($_POST['csrf']);
}

このような実装では、

  • トークンがある → 検証する
  • トークンがない → スルー

という状態になります。

何が問題か

この場合、攻撃者はトークンを送らないだけで検証を回避できるため、CSRFが成立します。

CSRF対策はトークンを付けるだけで終わりではなく、実装の細かい条件によって成立してしまうケースがあります。

このあたりの仕様や典型的な実装ミスについては、書籍でまとめて理解しておくと整理しやすいです。

【Amazon.co.jp】体系的に学ぶ 安全なWebアプリケーションの作り方 第2版


WordPressで考えるCSRF|nonceの仕組み

WordPressでは nonce を使ってCSRF対策を行います。

しかし、以下のような実装では同じ問題が起こります。

if (isset($_POST['_wpnonce'])) {
wp_verify_nonce($_POST['_wpnonce'], 'action');
}

nonceが未送信でも処理が実行されるため、対策として機能しません。


正しいCSRF対策

CSRF対策では次の2つが必須です。

  • トークンが存在すること
  • トークンが正しいこと
if (!isset($_POST['_wpnonce'])) {
exit('Invalid request');
}if (!wp_verify_nonce($_POST['_wpnonce'], 'action')) {
exit('Invalid nonce');
}

このように、トークンがない時点で処理を拒否する必要があります。


まとめ

CSRFが成立する理由はシンプルです。

  • ブラウザがCookieを自動送信する
  • サーバーがリクエストを信用してしまう

そして、CSRF対策の本質は次の通りです。

  • リクエストが正規画面から送られたかを検証する
  • トークンを必須にする

CSRF対策はすべてのリクエストで確実に検証される設計にすることが重要です。