WordPressで考えるCSRF対策の落とし穴|POSTリクエストのみの検証では防げない理由

CSRF対策として「トークン(nonce)を使っているから安全」と思っていませんか。

実は、トークンを導入していても、実装次第では簡単に回避されてしまうケースがあります。

この記事では、Web Security Academyのラボ「CSRF where token validation depends on request method」をもとに、POSTのみのCSRF対策をしている場合の危険性と、WordPressで実際に起こりうるパターンを解説します。

CSRFとは何か

CSRF(Cross-Site Request Forgery)は、ユーザーに意図しないリクエストを送らせる攻撃です。

ログイン状態のユーザーに対して、

  • メールアドレス変更
  • パスワード変更
  • 各種設定変更

といった操作を、本人の意思とは関係なく実行させることができます。


ラボのポイント|POSTだけ守ってGETはノーチェック

今回のラボでは、以下のような実装になっていました。

  • POSTリクエスト → CSRFトークンを検証
  • GETリクエスト → トークン未検証

一見するとCSRF対策が実装されているように見えますが、実際には完全ではありません。


攻撃の流れ|GETリクエストでCSRFを成立させる手順

この脆弱性は、次のような流れで悪用されます。

① 攻撃者が、メールアドレス変更用のURLを作成する

GET /my-account/change-email?email=pwned@evil-user.net

② そのURLを、攻撃用ページに埋め込む

<img src="https://example.com/my-account/change-email?email=pwned@evil-user.net">

③ 被害者がそのページを開く

④ ブラウザが自動的にリクエストを送信する

⑤ 被害者のメールアドレスが変更される

ユーザーは画像を表示しただけに見えますが、裏では状態変更リクエストが実行されています。


なぜ攻撃が成立するのか|ブラウザとCookieの挙動

成立理由は、次の3つです。

① ブラウザは自動でリクエストを送る

imgタグなどを使うと、ユーザーの操作なしにリクエストが送信されます。

② セッションCookieが自動で付与される

被害者がログイン中であれば、ブラウザはそのサイトのCookieを自動的に付けてリクエストを送信します。

そのため、サーバー側から見ると本人の操作と区別がつきません。

③ GETリクエストではCSRFトークンが検証されない

本来であればトークンで防げるはずですが、

  • POST → 検証あり
  • GET → 検証なし

という実装のため、防御が機能していません。


WordPressで起こりうるCSRFの実装ミス

WordPressでも同様の問題は現実にも起こり得ます。

例:nonceはあるがGETで処理できてしまう

if ($_POST['action'] === 'update_email') {
check_admin_referer('update_email_action');
update_user_meta($user_id, 'email', $_POST['email']);
}

このコードは一見安全ですが、別の処理で以下のようなコードが存在すると問題になります。

if (isset($_GET['email'])) {
update_user_meta($user_id, 'email', $_GET['email']);
}

この場合、nonceを通さずにメールアドレスが変更できてしまいます。

よくある原因

  • GETリクエストは安全という誤解
  • デバッグ用コードの残存
  • 同じ処理でも、フォーム以外の別ルートから実行できてしまう

CSRF対策の正しい実装|WordPressでのポイント

すべてのリクエストで検証する

POSTかGETかに関係なく、状態変更を伴う処理では必ずnonceを検証します。

GETで状態変更を行わない

状態を変更する処理はPOSTに限定します。

処理経路を統一する

同じ処理を複数の経路から呼び出せる構造にしないことが重要です。


セキュリティ診断で確認すべきチェック項目

CSRFは実装の抜け漏れで発生することが多いため、以下のようなシンプルな確認でも見つかります。

  1. GETリクエストで状態変更ができないかを見る
  2. CSRFトークン(nonce)がすべての経路で検証されているかをチェックする
  3. 同じ処理に複数のエンドポイントが存在しないかを確認する

トークンの有無だけで判断せず、実際にリクエストを書き換えて、検証が正しく機能しているかを確かめることが重要です。


攻撃者視点で見ると

このタイプの脆弱性は、比較的シンプルに見つかります。

  • フォームの送信内容を確認する
  • POSTリクエストを確認する
  • 同じ処理をGETで再現できるか試す

GETリクエストで通る場合、そのままCSRF攻撃が成立します。


まとめ|CSRF対策は全リクエスト検証が前提

CSRFトークンを実装していても、それだけで安全とは限りません。

今回のようにPOSTだけ検証し、GETリクエストを見落としている場合、対策は簡単に回避されてしまいます。

CSRFは、ユーザーに気づかれないままリクエストを送らせる攻撃です。

ブラウザはリクエスト時に自動でCookieを付けるため、外部サイトから送られたリクエストでも、サーバー側からは正規の操作と区別できません。

重要なのは、トークンの有無ではなく、すべてのリクエストで検証されているかです。

実装の抜け漏れがないかを確認することが、CSRF対策の基本です。