Spring SecurityでCSRF対策を有効化する方法を初心者向けに徹底解説!
新人
「Spring SecurityでCSRF攻撃を防ぐ設定ってどうすればいいんですか?そもそもCSRFってなんですか?」
先輩
「それはとても大切なポイントだね。まずはCSRFとは何かから説明していこうか。Spring SecurityのCSRF対策は非常に重要なんだ。」
新人
「なるほど、基本から順番にお願いします!」
先輩
「もちろん!CSRFとは何か、なぜ危険なのか、Spring Securityでどう対応されているか、順番に解説するね。」
1. CSRFとは何か?
CSRF(クロスサイトリクエストフォージェリ)とは、ログイン中のユーザーの権限を悪用して、意図しない操作を実行させるWeb攻撃の一種です。日本語では「サイト間リクエスト偽造」とも呼ばれます。
たとえば、あるユーザーがインターネットバンキングにログイン中に、別の悪意あるWebサイトを開いたとします。そこに仕掛けられた罠によって、ユーザーのブラウザが知らないうちに銀行に振込リクエストを送ってしまう、というような被害が考えられます。
このように、CSRF攻撃はユーザーの意図しない操作をWebアプリケーションに実行させるため、金銭的被害や個人情報漏洩など深刻な影響をもたらす可能性があります。
しかもこの攻撃は、ユーザーが正規のログイン情報を持っていて、ログインセッションが有効な状態であれば成立してしまいます。
2. なぜCSRF対策が必要なのか
CSRF攻撃は、見た目には正規のリクエストを装ってくるため、サーバー側でそのリクエストが「ユーザーの意図に基づいたものかどうか」を判別するのが非常に難しいという特徴があります。
以下のような攻撃が現実に起こり得ます。
- ログイン中のショッピングサイトで、勝手に商品が注文される
- マイページでメールアドレスやパスワードが書き換えられる
- 掲示板やブログで不適切な投稿がされる
つまり、ログイン状態のユーザーが不用意に別のサイトにアクセスするだけで、セッションを悪用され、悪意のある操作を実行されてしまう危険があるのです。
このような事態を防ぐために、CSRF対策はすべてのWebアプリケーションにとって必須なのです。特にセッションやログイン機能があるアプリでは、対策がなければ非常に危険です。
3. Spring SecurityではCSRF対策がデフォルトで有効である理由
Spring Securityでは、Webアプリケーションのセキュリティを強化するために、CSRF対策がデフォルトで有効になっています。これは、開発者がうっかり設定を忘れてしまっても、最低限の防御が働くように設計されているからです。
Spring Bootを使用してSpring Securityを有効にした場合、POST・PUT・DELETEなどの状態を変更するリクエストに対して、自動的にCSRFトークンが検証されます。
そのため、HTMLのフォームやJavaScriptでPOSTリクエストを送る際には、適切にトークンを埋め込んで送信する必要があります。トークンが含まれていない、もしくは一致していない場合は、403 Forbiddenエラーが返されます。
これはSpring Securityが「このリクエストは本当にユーザーが意図して行ったものか」をチェックしている証拠です。
実際に確認してみましょう。以下は、pleiades + Gradle 環境で作成したSpring BootプロジェクトにおけるCSRF対策のデフォルト設定です。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/").permitAll()
.anyRequest().authenticated()
)
.csrf(Customizer.withDefaults()); // CSRF対策を有効化(デフォルト)
return http.build();
}
}
csrf(Customizer.withDefaults())と記述することで、Spring SecurityのCSRF保護が適用されます。
これは特に設定を変更していない限り、初期状態で有効になっています。無効化せずにこの仕組みを活用することが、セキュアなWebアプリ開発の第一歩です。
初心者のうちは403エラーに戸惑うかもしれませんが、それはSpring Securityが「安全に動作している証拠」でもあります。
4. CSRFトークンの仕組み(生成と検証の流れ)
Spring Securityでは、CSRF対策の中心となるのが「CSRFトークン」です。これは、Webアプリケーションがユーザーのセッションごとに発行する一意な値であり、外部の攻撃者が容易に推測できないようになっています。
トークンの仕組みは以下のように働きます。
- ユーザーがページを開くと、サーバー側でCSRFトークンが生成され、セッションに紐づけられる
- そのトークンをフォームなどにhiddenフィールドとして埋め込んでおく
- ユーザーがフォームを送信すると、トークンも一緒にサーバーに送信される
- サーバー側で、送信されたトークンとセッションに保存されたトークンを比較し、一致すれば正当なリクエストと判断される
このように、CSRFトークンは「このフォームを本当にユーザー本人が送ったか」を確認するための重要な役割を持っています。
トークンの確認処理はSpring Securityが自動で行ってくれるため、開発者が意識するべきなのは「HTMLフォームに正しくトークンを埋め込むこと」です。
5. Spring SecurityでのCSRF有効化の設定例
Spring Securityでは、特別な設定をしなくてもCSRF対策が有効ですが、カスタマイズしたい場合や明示的に設定したい場合には、SecurityFilterChainを使って設定を行います。
以下は、pleiadesで作成したSpring Boot + Gradleプロジェクトにおける、CSRF対策を明示的に有効化したサンプルです。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/", "/login").permitAll()
.anyRequest().authenticated()
)
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
return http.build();
}
}
この例では、CSRFトークンをCookieでクライアント側に送信する設定になっています。HttpOnly属性をfalseにしているため、JavaScriptでもトークンの読み取りが可能になります。
ただし、HTMLフォームに手動でトークンを埋め込む場合は、この設定でなくても大丈夫です。セッションベースのデフォルト設定でも問題ありません。
重要なのは、トークンの送信と検証が一致するように構成することです。特にセキュリティ設定で無効化していない限り、POSTリクエストには必ずトークンが必要になります。
6. CSRFトークンをHTMLフォームに埋め込む方法(Thymeleafなし)
テンプレートエンジンであるThymeleafを使っていれば、CSRFトークンの埋め込みは簡単ですが、ここではあえてThymeleafを使わず、プレーンなHTMLフォームでCSRF対策を行う方法を紹介します。
Spring Securityでは、CsrfTokenオブジェクトを使って、コントローラ内でトークンを取得できます。次のように@Controllerでトークンをモデルに追加しましょう。
@Controller
public class AccountController {
@GetMapping("/form")
public String showForm(Model model, CsrfToken token) {
model.addAttribute("_csrf", token);
return "form";
}
}
このように、トークンを_csrfという名前でモデルに渡せば、HTML側で以下のように埋め込むことができます。
<form method="post" action="/submit">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<label>ユーザー名:</label>
<input type="text" name="username" />
<button type="submit">送信</button>
</form>
_csrf.parameterNameには通常_csrfという名前が入り、_csrf.tokenには実際のトークン値が代入されます。Spring Securityは、このパラメータが含まれているかどうか、そしてセッションと一致するかを検証します。
もしトークンがない場合や、間違っている場合は、403 Forbiddenエラーとなりリクエストは拒否されます。
テンプレートエンジンを使わない場合でも、上記のようにトークンをフォームに挿入することで、Spring SecurityのCSRFトークン検証に対応する安全なフォームを実装できます。
JavaScriptやAjaxでCSRFトークンを扱う場合は、metaタグやCookieからトークンを取得して、リクエストヘッダにセットするという方法もありますが、初心者のうちはまずHTMLフォームでの対応をしっかり理解しておくことが大切です。
7. 特定のエンドポイントをCSRF対象から除外する方法
通常、Spring Securityでは全ての状態変更リクエスト(POST・PUT・DELETEなど)に対してCSRFトークンの検証が行われますが、開発中には一部のAPIやエンドポイントだけをCSRF検証の対象外にしたい場面もあります。
たとえば、外部のサービスとの通信や、バッチ処理用のエンドポイントなど、トークンを含めるのが困難な場合には、特定のパスだけを除外することが可能です。
その場合は、以下のようにcsrf().ignoringRequestMatchers()を使って、除外対象のURLを指定します。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/", "/login").permitAll()
.anyRequest().authenticated()
)
.csrf(csrf -> csrf
.ignoringRequestMatchers("/api/external/**")
);
return http.build();
}
}
上記のように設定することで、/api/external/以下のリクエストはCSRFトークンの検証をスキップできます。
ただし、除外設定をすることでセキュリティが弱くなる可能性があるため、本番環境では必ず認証や署名など他のセキュリティ対策と組み合わせて運用するようにしましょう。
CSRF除外設定は便利な反面、攻撃の入口になるリスクもあります。むやみに多くのパスを除外対象にしないことが安全な設計につながります。
8. CSRFを無効化した場合のリスク(やってはいけない設定例の紹介)
初心者がついやってしまいがちなミスの一つに、.csrf().disable()を使用してCSRF保護を完全に無効化してしまうというケースがあります。
CSRFトークンの扱いが難しく感じたり、開発中に403エラーが頻発すると、簡単にこの設定で回避したくなる気持ちは理解できます。
しかし、これは極めて危険な設定です。以下のようなコードは、本番環境では絶対に避けてください。
http
.authorizeHttpRequests(authz -> authz
.anyRequest().permitAll()
)
.csrf().disable(); // ← これはNG!
このようにCSRF対策を無効化すると、フォーム送信やAjax通信に対して何の検証も行われなくなり、第三者による不正リクエストをそのまま受け入れてしまうことになります。
CSRF攻撃は、ログインセッションが存在していれば誰でも成立させられるため、攻撃者はユーザーを誘導するだけで悪意ある操作を実行可能になります。
Spring Security CSRF制御を完全に無効にするという選択肢は、基本的に許されません。一時的に開発中のみ使う場合でも、コメントで明示し、絶対に本番に混入させないように管理しましょう。
どうしてもCSRFトークンの対応が困難なケースでは、エンドポイント単位で除外するなど、リスクを最小限に抑える方法を検討してください。
9. 安全なCSRF対策運用のベストプラクティス(開発と本番での切り替えなど)
CSRF対策は、単に設定するだけでなく、開発環境と本番環境での運用方針をしっかりと分けておくことが重要です。
以下は、安全なCSRF対策のベストプラクティスです。
- 開発環境では限定的に除外設定を使う
- application.propertiesや環境変数で切り替え制御
- フォームにトークンを忘れずに埋め込む
- 本番では必ずCSRFを有効にして運用
例えば開発中のAPIテストやフロントエンド連携確認では、一部のURLのみCSRFを除外設定し、他は通常通りトークンを検証するようにしましょう。
spring.profiles.activeを使って、devとprodのプロファイルを分けておくと、CSRFの有効・無効や除外範囲を柔軟に管理できます。
Thymeleafを使わない場合でも、CSRFトークンは明示的にHTML内へ埋め込む必要があります。特にJavaScriptやjQueryのAjax通信を使う際には、HTTPヘッダーにトークンを手動で設定するなどの工夫が必要です。
本番環境では、いかなる理由があってもCSRF保護を無効にしないことが大前提です。CSRF攻撃は想定外のところから仕掛けられることが多く、セッションが有効である限り誰でも被害に遭い得ます。
CSRF除外設定を開発中だけ使い、本番では常にトークン検証を行う設計を守ることが、堅牢なセキュリティを維持するための最善策です。
また、テストコードやCI/CDパイプラインの中でもCSRFチェックを実施して、誤って無効化されたまま本番にデプロイされないようにする仕組みも大切です。
Spring Security CSRF制御は、少し扱いが難しい部分もありますが、それだけに正しく運用することがセキュリティの基盤となります。
開発段階から意識し、チーム内で運用ルールを共有しておくことで、事故や脆弱性の混入を未然に防ぐことができるでしょう。