カテゴリ: Spring認証(Spring Security) 更新日: 2026/02/22

CSRF対策が有効なフォームの作成方法を完全解説!Spring Securityの仕組みと設定ポイント

CSRF対策が有効なフォームの作成方法
CSRF対策が有効なフォームの作成方法

新人と先輩の会話形式で理解しよう

新人

「Spring SecurityのCSRF対策って何のためにあるんですか?フォームに何か追加しないとダメなんですか?」

先輩

「うん、フォームにCSRFトークンを追加しないと、Spring Securityがリクエストを受け付けてくれないよ。セキュリティ上の対策なんだ。」

新人

「フォームのセキュリティって言われてもピンと来ません…。具体的にどんな仕組みなんでしょうか?」

先輩

「じゃあまずは、CSRF攻撃が何かから順に説明していこうか。」

1. CSRFとは?(クロスサイトリクエストフォージェリの意味と脅威)

1. CSRFとは?(クロスサイトリクエストフォージェリの意味と脅威)
1. CSRFとは?(クロスサイトリクエストフォージェリの意味と脅威)

CSRF(シーエスアールエフ)とは、クロスサイトリクエストフォージェリと呼ばれるセキュリティ上の攻撃手法のことです。日本語では「サイト間リクエスト偽造」とも訳されます。

この攻撃は、ログイン中のユーザーが知らないうちに、意図しない操作をWebアプリケーションに対して実行させられるというものです。

たとえば、ユーザーが銀行の振込画面にログインしている状態で、別の悪意のあるサイトを開いてしまったとします。すると、その悪意あるサイトが自動で振込リクエストを送ってしまい、ユーザーが気づかないうちにお金が送金されるという危険性があります。

このように、セッションを利用したフォーム操作やアカウント更新処理などは、攻撃対象になりやすいのです。

そこで必要になるのがCSRF対策です。Spring Securityではこのような攻撃から守るために、デフォルトでCSRF保護が有効になっています。

フォームのセキュリティを確保するためには、正しいCSRFトークンを埋め込んだリクエストだけを受け付けるようにすることが重要です。

2. Spring SecurityにおけるCSRF保護の基本設定と自動有効化の仕組み

2. Spring SecurityにおけるCSRF保護の基本設定と自動有効化の仕組み
2. Spring SecurityにおけるCSRF保護の基本設定と自動有効化の仕組み

Spring Securityでは、CSRF対策が初期設定で有効になっており、開発者が特別な設定をしなくても、自動的に保護がかかるようになっています。

具体的には、POST・PUT・DELETEなどの変更系リクエストに対して、CSRFトークンの検証が行われるようになっています。

Spring Securityの内部では、HttpSecurityの中で以下のような設定が自動で適用されています。


@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.enable()) // デフォルトで有効なので明示的な記述は不要
            .authorizeHttpRequests(authz -> authz
                .anyRequest().permitAll()
            );
        return http.build();
    }
}

このように、特別な設定をしなくてもCSRF対策は機能しています。

ただし、フォームの送信時にはCSRFトークンを含めなければ、Spring Securityが403 Forbiddenを返すようになっています。

そのため、開発者はフォームの中にCSRFトークンをhidden項目として埋め込む必要があります。

このとき、Thymeleafを使用していない場合でも、Javaの@Controllerでトークンを取得し、HTMLに渡すことで対応が可能です。

以下は、pleiades + Gradleの開発環境で構築したSpringプロジェクトで、CSRFトークンをフォームに連携する方法の一部です。


@Controller
public class UserFormController {

    @GetMapping("/form")
    public String showForm(Model model, CsrfToken token) {
        model.addAttribute("_csrf", token);
        return "userForm";
    }
}

このようにしてJava側でトークンをModelに渡し、HTMLフォームに埋め込むことで、Spring SecurityによるCSRF保護が有効な安全なフォームを作成することができます。

次回は、実際のHTMLコードでCSRFトークンを埋め込む方法や、セッションとの連携方法を解説していきます。

3. CSRFトークンの仕組みと発行タイミング(セッション連携)

3. CSRFトークンの仕組みと発行タイミング(セッション連携)
3. CSRFトークンの仕組みと発行タイミング(セッション連携)

Spring Securityでは、ユーザーがログインしてセッションを開始したタイミングで、自動的にCSRFトークンの生成が行われます。このトークンはセッションごとに割り当てられ、サーバー側で保持されます。

このトークンは、ユーザーがフォームページへアクセスした際にJavaの@Controller経由でHTML側に渡され、フォームにhidden項目として埋め込まれることで、リクエストの正当性を証明する役割を果たします。

つまり、サーバー側で管理しているCSRFトークンと、フォームから送信されたトークンが一致すればリクエストは成功し、一致しなければ403 Forbiddenが返されます。これがSpring Security フォーム CSRF対策の基本です。

このように、トークンの発行と検証にはセッション情報が連携しており、ログイン後のセッションに依存した保護が行われている点がポイントです。

4. CSRFトークンのHTMLフォームへの埋め込み(Thymeleafなし)

4. CSRFトークンのHTMLフォームへの埋め込み(Thymeleafなし)
4. CSRFトークンのHTMLフォームへの埋め込み(Thymeleafなし)

Thymeleafを使っていれば、簡単に<form:form>タグでCSRFトークンを自動埋め込みできますが、今回は使用しない前提です。

そのため、Javaの@Controllerから明示的にトークンを取得し、HTMLテンプレートでhidden項目として手動で記述する必要があります。

以下は、フォームにCSRFトークンを埋め込むHTMLコードの例です。


<form action="/submit" method="post">
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <input type="text" name="username" />
    <input type="submit" value="送信" />
</form>

ここで、${_csrf.parameterName}には_csrfオブジェクトのパラメータ名(通常は_csrf)、${_csrf.token}には実際のトークン値が格納されています。

これらは先ほどの@Controllerで次のように渡しています。


@GetMapping("/form")
public String showForm(Model model, CsrfToken token) {
    model.addAttribute("_csrf", token);
    return "userForm";
}

HTML側ではEL式(${ })でトークン値を参照し、明示的にフォームへ埋め込むことで、Spring Security フォーム CSRF対策が完成します。

5. トークンの検証処理と403 Forbiddenの理由

5. トークンの検証処理と403 Forbiddenの理由
5. トークンの検証処理と403 Forbiddenの理由

Spring Securityは、POSTやPUTなどのHTTPリクエストに対して、内部的にトークンの検証処理を行っています。

この検証は、リクエストヘッダーやフォーム内のhidden項目に含まれているトークンと、セッションに保存されているトークンを照合することで行われます。

照合に成功すればリクエストが通過し、失敗すると自動的に403 Forbiddenエラーが返されます。このエラーは、CSRFトークンが無効・未送信・不一致であることを示しています。

開発時によくある失敗例として、次のようなケースがあります:

  • フォームに<input type="hidden">でトークンを埋め込むのを忘れた
  • @ControllerCsrfTokenをModelに渡していない
  • HTML内で${_csrf.token}の記述を間違えている
  • JavaScriptで動的に生成したフォームにトークンを入れていない

このような場合は、403 Forbiddenが表示され、リクエストがブロックされます。

Spring Securityは、セッションが保持されている限り、常にサーバー側にCSRFトークンを保存しており、リクエストごとのトークン照合が自動で実行されるようになっています。

したがって、CSRF対策が有効なフォームを作成するには、必ず以下のポイントを意識してください。

  • トークンをHTMLに確実に埋め込むこと
  • @Controllerでトークンを忘れずにModelに追加すること
  • トークンの送信先が正しいエンドポイントであること

CSRFトークンの仕組みを理解し、正しくフォームに組み込むことで、403 Forbiddenエラーの回避と同時に、強固なセキュリティを実現できます。

6. Javaコードとフォーム連携による安全な送信処理

6. Javaコードとフォーム連携による安全な送信処理
6. Javaコードとフォーム連携による安全な送信処理

ここでは、CSRFトークンの仕組みを理解した上で、@ControllerとHTMLフォームを連携させた安全な送信処理を実装する方法を紹介します。

まず、@Controllerでトークンを取得してModelに渡します。そして、POSTリクエストを受け取るメソッドを用意して、フォームの送信処理を安全に行います。


@Controller
public class UserFormController {

    @GetMapping("/form")
    public String showForm(Model model, CsrfToken token) {
        model.addAttribute("_csrf", token);
        return "userForm";
    }

    @PostMapping("/submit")
    public String submitForm(@RequestParam("username") String username, Model model) {
        model.addAttribute("message", "ユーザー名:" + username + " を受け付けました。");
        return "result";
    }
}

次に、フォーム側のHTMLテンプレートには、次のようにCSRFトークンを埋め込みます。


<form action="/submit" method="post">
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <label>ユーザー名:</label>
    <input type="text" name="username" />
    <input type="submit" value="送信" />
</form>

このように記述することで、Spring Security フォーム CSRF対策に準拠した安全な送信処理が可能となります。

7. CSRF除外設定のリスクと使い方(どうしても必要な場合の対応)

7. CSRF除外設定のリスクと使い方(どうしても必要な場合の対応)
7. CSRF除外設定のリスクと使い方(どうしても必要な場合の対応)

特定のエンドポイントでCSRFトークンを送らない必要があるケースもまれにあります。たとえば、外部APIからのPOSTリクエストやWebhook受信処理などです。

このような場合、Spring Security 設定方法として、CSRFトークン検証を一部のパスで除外する設定を行うことが可能です。


@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf
                .ignoringRequestMatchers("/webhook/**") // 除外するパスを指定
            )
            .authorizeHttpRequests(authz -> authz
                .anyRequest().permitAll()
            );
        return http.build();
    }
}

このようにignoringRequestMatchersを使って、特定のURLパスに対するCSRF除外設定を行うことができます。

ただし、この設定には重大なリスクが伴います。CSRF保護を無効化したエンドポイントは、外部からのリクエストを無制限に受け入れてしまう可能性があります。

特に、認証済みのセッションと連携している処理で除外設定を行うと、セキュリティホールになる危険性が高いです。

このため、CSRFを除外する際は以下の対策を併用しましょう。

  • トークン認証やAPIキーによる認可処理を追加する
  • 除外対象のエンドポイントを限定的に絞り込む
  • ログの出力IP制限を導入して不正アクセスを監視する

このように、CSRF除外設定は便利な反面、セキュリティ上の抜け道になりかねないため、最小限の使用に留めましょう。

8. 本番環境で安全に運用するためのベストプラクティス

8. 本番環境で安全に運用するためのベストプラクティス
8. 本番環境で安全に運用するためのベストプラクティス

最後に、本番環境でCSRF対策を含むセキュリティ運用を安全かつ安定して行うためのベストプラクティスを紹介します。

(1)セッショントークンのタイムアウトを設ける

CSRFトークンはセッションに紐づいているため、セッションが無効になるとトークンも無効になります。長時間放置されたセッションを自動で切断することで、不正利用を防止できます。

Spring Bootでは、application.propertiesなどで次のように設定します。


server.servlet.session.timeout=15m

これにより、最後のアクセスから15分が経過すると自動的にセッションが破棄されます。

(2)トークンのCookie管理との違いと選択基準

Spring SecurityのCSRFトークンは、通常はサーバー側セッションで管理されますが、Cookieにトークンを設定してフロントエンドと連携する方式も存在します。

Cookieベースの運用はSPA(Single Page Application)などで有効ですが、HTTPヘッダーでトークンを送信しないと検証エラーになるなど、実装が複雑になります。

今回のように、サーバーサイドでHTMLテンプレートを生成する構成では、セッション方式でのトークン管理のほうが初心者には扱いやすく、安全性も高いです。

(3)CSRFエラー発生時のユーザー対応

CSRFトークンが無効な場合、403 Forbiddenが返されてユーザーは困惑することがあります。

本番運用では、独自のエラーページを用意し、ユーザーに「再度ログインしてください」などの案内を表示すると親切です。

(4)セキュリティアップデートの継続的な適用

Spring Securityは継続的にアップデートされており、既知の脆弱性に対応するには定期的なライブラリ更新が不可欠です。

Gradleでバージョンを固定している場合でも、build.gradleでライブラリを見直す習慣を持ちましょう。

以上のようなポイントを意識することで、CSRF対策を含めたセキュリティ運用を本番環境で適切に行うことができます。

まとめ

まとめ
まとめ

ここまで、Spring SecurityにおけるCSRF(クロスサイトリクエストフォージェリ)対策の重要性と、具体的な実装方法について詳しく解説してきました。Webアプリケーションを公開する上で、セキュリティ対策は避けて通れない非常に重要なテーマです。特に、ユーザーの大切な情報を守り、意図しない操作を防ぐためのCSRF保護は、現代のWeb開発において必須のスキルと言えるでしょう。

Spring Securityによる堅牢な保護の要点

Spring Securityの最大の利点は、強力なセキュリティ機能がデフォルトで有効化されている点にあります。開発者は、フレームワークが提供する「安全なデフォルト設定」を理解し、適切に活用することで、脆弱性の少ないアプリケーションを効率的に構築できます。今回のポイントを振り返ると、以下の3点が特に重要です。

  • トークンの自動検証: POST、PUT、DELETEといった状態を変更するリクエストに対して、Spring Securityは常にCSRFトークンの有無と正当性をチェックしています。
  • セッションとの密接な連携: トークンはユーザーのセッションごとに一意に発行され、サーバー側で厳密に管理されます。これにより、第三者がトークンを推測したり偽造したりすることが極めて困難になります。
  • 明示的な埋め込みの必要性: Thymeleafなどのテンプレートエンジンを使用しない場合、Javaのコントローラー側で明示的にトークンを取得し、HTMLフォームのhiddenフィールドへセットする作業が必要です。これを忘れると「403 Forbidden」エラーが発生し、正当な操作もブロックされてしまいます。

実践的な実装サンプルコード

実際のプロジェクトで役立つ、一連の連携コードを改めて整理しておきましょう。コントローラーからビューへ、そして送信処理へとつながる一連の流れを意識することが、理解を深める近道です。


// コントローラーでのトークン受け渡し例
@Controller
@RequestMapping("/user")
public class RegistrationController {

    /**
     * 登録フォームを表示するメソッド
     * CsrfTokenを引数に取ることで、Spring Securityから自動的に注入されます
     */
    @GetMapping("/register")
    public String showRegistrationForm(Model model, CsrfToken token) {
        // HTML側で利用できるように"_csrf"という名前でModelに格納
        model.addAttribute("_csrf", token);
        return "register_form";
    }

    /**
     * フォーム送信を受け付けるメソッド
     * 正しいCSRFトークンが含まれていない場合、このメソッドが呼ばれる前に403エラーとなります
     */
    @PostMapping("/process")
    public String processRegistration(@RequestParam("email") String email, Model model) {
        // 登録処理のロジックをここに記述
        model.addAttribute("msg", email + " の登録が完了しました。");
        return "register_success";
    }
}

上記のコントローラーに対応するHTMLフォームの構成は以下の通りです。クラス名や構造は、これまでの解説と統一させています。


<!-- セキュリティが考慮されたユーザー登録フォーム -->
<form action="/user/process" method="post" class="p-4 border rounded shadow-sm">
    <!-- CSRF対策のhidden項目:ここが最重要! -->
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    
    <div class="mb-3">
        <label for="email" class="form-label">メールアドレス</label>
        <input type="email" id="email" name="email" class="form-control" required>
    </div>
    
    <button type="submit" class="btn btn-primary w-100">登録する</button>
</form>

運用フェーズで意識すべきこと

開発が完了し、運用フェーズに入った後も、セキュリティの意識を高く保ち続けることが大切です。例えば、ユーザーのセッションタイムアウト設定(server.servlet.session.timeout)を適切に調整することで、万が一端末が放置された際のリスクを軽減できます。また、ブラウザの「戻る」ボタンによるトークンの不整合や、複数のタブを開いた際の挙動など、ユーザーが直面する可能性のある「403エラー」に対して、分かりやすいエラーメッセージやカスタムエラーページを用意しておくことも、UX(ユーザーエクスペリエンス)向上の観点から非常に有効です。

Spring Securityは、私たちが複雑なセキュリティプロトコルをゼロから実装する手間を省いてくれます。しかし、その背後にある仕組みを理解していなければ、トラブルシューティングに時間がかかってしまいます。今回の内容を土台にして、より安全で信頼性の高いJava Webアプリケーション開発を目指していきましょう。

先生と生徒の振り返り会話
生徒:「先生、ありがとうございました!CSRF対策って、最初はただ『hiddenタグを入れるだけ』だと思っていましたけど、実はセッション管理と深く関わっているんですね。」
先生:「その通りだね。Spring Securityが裏側でセッションごとにランダムなトークンを生成して、それを毎回照合しているんだ。この『使い捨ての合言葉』があるから、外部のサイトから勝手にリクエストを送られても、サーバー側で偽物だと判断できるんだよ。」
生徒:「なるほど。もし私が実装中に 403 Forbidden というエラー画面に出会ったら、まずは『CSRFトークンが正しくHTMLに埋め込まれているか』を確認すればいいんですね?」
先生:「正解!特にThymeleafを使わない場合は、コントローラーで CsrfToken をModelに渡し忘れることが多いから注意が必要だね。デベロッパーツールでHTMLのソースを見て、hidden項目のvalueにちゃんと長い文字列が入っているかチェックする癖をつけるといいよ。」
生徒:「わかりました!あと、APIや外部サービスとの連携でどうしてもCSRFを無効にしたいときは、ignoringRequestMatchers を使うんでしたね。でも、それは本当に必要な時だけにして、リスクも考えるようにします。」
先生:「素晴らしい。セキュリティは利便性とのトレードオフになりがちだけど、まずは『安全第一』で考えることがエンジニアとして大切だよ。これからもSpring Securityのいろんな機能を学んで、強固なシステムを作っていこう!」

この記事を読んだ人からの質問

この記事を読んだ人からの質問
この記事を読んだ人からの質問

プログラミング初心者からのよくある疑問/質問を解決します

CSRF(クロスサイトリクエストフォージェリ)とは具体的にどのような攻撃手法のことを指すのでしょうか?プログラミング初心者にもわかりやすく教えてください。

CSRF(シーエスアールエフ)は、日本語で「サイト間リクエスト偽造」と呼ばれるサイバー攻撃の一種です。この攻撃の恐ろしい点は、ユーザーが意図しない操作を、ログイン中の正規のWebアプリケーションに対して勝手に実行させられてしまうことにあります。例えば、あなたがネットバンキングにログインしたまま、悪意のあるプログラムが仕込まれた別のWebサイトを閲覧したとします。その際、悪意のあるサイトがあなたのブラウザを介して「送金リクエスト」を銀行のサーバーに勝手に送信してしまうのです。銀行側は、あなたのブラウザから正しいセッション情報(ログイン状態)と共にリクエストが届くため、それが本人の操作か、攻撃者による偽造リクエストかを見分けることができません。その結果、本人が気づかないうちにお金が振り込まれたり、パスワードが変更されたりといった被害が発生します。これを防ぐために、Spring Securityなどのフレームワークでは、リクエストが本当にそのサイトのフォームから送られたものかを確認するための「CSRF対策」が必須となっています。
コメント
コメント投稿は、ログインしてください

まだ口コミはありません。

カテゴリの一覧へ
新着記事
New1
Springの基本
Spring Bootの@ConfigurationPropertiesScanとは?設定クラス自動検出の仕組みを解説
New2
SpringのAPI開発(REST & GraphQL)
Spring Boot GraphQLでResolverを理解しよう!初心者でもわかるデータ取得の基本
New3
SpringのAPI開発(REST & GraphQL)
Spring Boot GraphQL入門!Query・Mutation・Subscriptionの基本を初心者向けに解説
New4
SpringのDB操作
JPQLのパラメータバインド(:name / ?1)の使い方を完全解説!初心者でも迷わない基本の考え方
人気記事
No.1
Java&Spring記事人気No1
Thymeleaf
Thymeleaf とは?初心者向けにThymeleafの基本を徹底解説
No.2
Java&Spring記事人気No2
SpringのWeb開発(Spring MVC)
DispatcherServletの仕組みを理解する!初心者向け完全ガイド
No.3
Java&Spring記事人気No3
Springの基本
Spring Bootのデフォルトログ設定を徹底解説(Logback / SLF4J)
No.4
Java&Spring記事人気No4
SpringのDB操作
JPAの標準クエリメソッド(findById, findAll)を完全解説!初心者でもわかるデータ取得の基本
No.5
Java&Spring記事人気No5
SpringのWeb開発(Spring MVC)
Spring Bootでの@GetMappingと@PostMappingの基本を完全解説!初心者でも理解できる使い方
No.6
Java&Spring記事人気No6
Spring認証(Spring Security)
セッション管理の基本(@SessionAttributes)を完全解説!初心者でもわかるセッションの仕組み
No.7
Java&Spring記事人気No7
SpringのWeb開発(Spring MVC)
@Controller と @RestController の違いを完全解説!初心者向けSpring MVC入門
No.8
Java&Spring記事人気No8
SpringのWeb開発(Spring MVC)
ループ処理(th:each)の基本を完全ガイド!Thymeafの繰り返し処理の使い方