2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ReactでCSRFトークンを送信する方法(Cookie + Headerの実装)

2
Last updated at Posted at 2026-04-01

全体像

  1. CSRFトークン取得(初回アクセス)
  2. Cookieに保存される
  3. リクエスト時にCookieから取得
  4. ヘッダーに付与して送信

1. CSRFトークンを取得する

App.tsx
function App() {
  useEffect(() => {
    // 初回アクセス時にCSRFトークンを取得
    fetch("/api/v1/csrf", {
      credentials: "include", // Cookieを受け取るために必要
    });
  }, []);

  return <AppRouter />;
}

export default App;

なぜ App.tsx に書くのか?

CSRFトークンは一度取得すれば使い回せるため、アプリ起動時に取得するのが適切です。

App.tsx は最初にマウントされるルートコンポーネントなので、このような「初回のみ実行する処理」を置くのに適しています。

また、各コンポーネントで取得するとリクエストの重複につながるため、共通の入口でまとめて処理します。

2. Cookieに保存される

cookie.ts
export function getCookie(name: string): string | null {
  return (
    document.cookie
      // "key=value; key2=value2" の形式なので分割する
      .split("; ")
      // 指定したCookie名(例: XSRF-TOKEN)を探す
      .find((row) => row.startsWith(name + "="))
      // "key=value" から value のみ取得
      ?.split("=")[1] ?? null // 存在しない場合はnull
  );
}

CSRFトークンは、サーバーからレスポンスとして直接返されるのではなく、Cookieとしてブラウザに保存されます。

そのため、リクエスト時にヘッダーへ付与するには、document.cookie からトークンを取得する必要があります。

ただし document.cookie"key=value; key2=value2" のような文字列で管理されているため、そのままでは扱いづらく、このように分割・検索して目的の値を取り出します。

3. リクエスト時にCookieから取得

apiFetch.ts
export async function apiFetch(url: string, options: RequestInit = {}) {
  // CookieからCSRFトークンを取得
  const csrfToken = getCookie("XSRF-TOKEN");

  const response = await fetch(url, {
    ...options,
    credentials: "include", // Cookieを送受信するために必要
    headers: {
      "Content-Type": "application/json",
      // CSRFトークンをヘッダーに付与
      "X-XSRF-TOKEN": csrfToken ?? "",
      // 呼び出し元で指定されたヘッダーをマージ
      ...(options.headers || {}),
    },
  });

  // レスポンスをテキストとして取得(空レスポンス対策)
  const text = await response.text();

  // 中身がある場合のみJSONパース
  const data = text ? JSON.parse(text) : null;

  // ステータスがエラーの場合は例外を投げる
  if (!response.ok) {
    throw new Error(data.error || "リクエストに失敗しました");
  }

  return data;
}

CSRFトークンは、Cookieに保存されているだけでは不十分で、リクエスト時にヘッダーへ付与する必要があります。

この関数では、まずCookieからトークンを取得し、それを X-XSRF-TOKEN ヘッダーとして送信しています。
サーバー側(例:Spring Security)は、このヘッダーとCookieの値を照合することで、リクエストが正当なものであるかを検証します。

また、この処理を共通関数としてまとめることで、各API呼び出しでCSRFを意識する必要がなくなり、安全性と実装のシンプルさを両立できます。

4. ヘッダーに付与して送信

try {
  // 共通関数(apiFetch)を使ってユーザー登録APIを呼び出す
  await apiFetch(SIGNUP, {
    method: "POST",
    // リクエストボディをJSON形式で送信
    body: JSON.stringify({
      email,
      password,
      confirmPassword,
    }),
  });
  // ※ CSRFトークンはapiFetch内で自動的に付与される
} catch (e: any) {
  // エラーレスポンスのメッセージを画面に表示
  setError(e.message);
}

実際のAPI呼び出しでは、特別なCSRF処理を書く必要はありません。
apiFetch を経由することで、Cookieから取得したCSRFトークンが自動的にヘッダーへ付与されます。

そのため、各コンポーネントでは通常のリクエストと同じように実装でき、CSRF対策を意識せずに安全な通信を行うことができます。

おまけ:Spring Security側のCSRFトークン発行

@RestController
public class CsrfController {

  @GetMapping("/api/v1/csrf")
  public ResponseEntity<Void> loadCsrf(CsrfToken token) {
    // トークンを生成(Cookieにセットされる)
    token.getToken();
    return ResponseEntity.ok().build();
  }
}

このエンドポイントは、CSRFトークンを生成し、Cookieとしてクライアントに返すためのものです。

Spring Securityでは、CsrfToken を引数に受け取ることでトークンが生成され、token.getToken() を呼び出すことでCookieへのセットが行われます。

フロントエンドでは、このエンドポイントを初回アクセス時に呼び出すことで、CSRFトークンを取得できます。

まとめ

CSRF対策は、Cookieに保存されたトークンをリクエスト時にヘッダーへ付与することで実現できます。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?