CSRFとは?クロスサイトリクエストフォージェリの基本とSpring SecurityによるWebセキュリティ対策
新人
「先輩!CSRFっていう攻撃があるって聞いたんですけど、よくわからなくて…」
先輩
「ああ、CSRFはWebアプリケーションでよくあるセキュリティリスクの一つだよ。Spring Securityでも対策が必須なんだ。」
新人
「クロスサイトっていう名前も難しそうで…。どんな仕組みなんですか?」
先輩
「大丈夫、初心者でもわかるように順番に説明していくよ。」
1. CSRFとは何か?(攻撃の基本的な説明)
CSRF(クロスサイトリクエストフォージェリ)は、Webセキュリティの分野で非常に重要なキーワードです。JavaでWebアプリケーションを開発するなら、CSRF対策は必ず理解しておきたいポイントの一つです。
この攻撃は、ログイン中のユーザーが意図しないリクエストを送信させられてしまうことによって、悪意ある操作が勝手に実行されてしまうというものです。たとえば、オンラインショップで商品を購入した直後に、第三者のWebサイトにアクセスしただけで勝手に注文が繰り返されるような状況を想像してみてください。
つまり、ユーザーのセッションや認証情報を悪用して、第三者が意図しない操作をさせるのがCSRF攻撃です。
名前の通り、「Cross Site=別のサイト」から、「Request Forgery=リクエストの偽造」をする攻撃なので、このように呼ばれています。
Spring Securityは、こうしたクロスサイトリクエストフォージェリに対応したセキュリティ対策が組み込まれており、プロジェクト作成時にPleiadesでSpring Securityを有効にしていれば、初期状態で自動的にCSRF対策が有効になります。
2. CSRFによって何が起こるのか(被害の具体例)
CSRFの怖さは、ユーザーが「自分で操作した」と思い込んでしまう点にあります。攻撃者が仕込んだHTMLやJavaScriptによって、ログイン中のセッション情報を使って勝手に操作が実行されるため、見た目には通常の操作と変わらないのです。
以下は典型的なCSRF攻撃の例です。
- ユーザーが銀行のオンラインバンキングにログインし、ブラウザにセッション情報が残っている。
- そのまま攻撃者のサイトにアクセスする。
- 攻撃者のサイトには、勝手に送金処理を行うような
formタグが埋め込まれている。 - ユーザーがボタンを押すと、銀行サイトに対して送金リクエストが送られる。
攻撃者のHTMLコードはこんなイメージです:
<form action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="amount" value="100000">
<input type="hidden" name="toAccount" value="attacker123">
<input type="submit" value="Click me!">
</form>
このように、ユーザーの同意なしにリクエストが送信されてしまい、Webアプリケーション側では正当なリクエストに見えてしまうのです。
特にPOSTリクエストを扱うようなWebアプリでは、CSRF対策を怠ると非常に危険です。
Spring Securityではこのようなリクエストをブロックするために、CSRFトークンという仕組みが用意されています。トークンがないリクエストは拒否されるため、セキュリティレベルが大幅に向上します。
逆に、CSRF対策が無効になっていると、上記のような悪意あるHTMLが有効に機能してしまい、Webアプリの信頼性が損なわれます。
JavaのSpring Securityは、このようなWebセキュリティの基本にしっかり対応しており、Spring Boot+@Controller構成でも簡単に組み込めるのが魅力です。
3. CSRFとXSSの違い(初心者向けの比較解説)
CSRF(クロスサイトリクエストフォージェリ)とXSS(クロスサイトスクリプティング)は、どちらもWebアプリケーションにおける重大なセキュリティ脅威です。名前が似ているため混同しやすいですが、それぞれ攻撃の目的も手法もまったく異なります。
CSRFは、ユーザーの認証情報を悪用して「勝手にリクエストを送信」させる攻撃です。一方、XSSは、ユーザーのブラウザ上で「悪意あるスクリプトを実行」させる攻撃です。
簡単にたとえると、
- CSRF: 銀行のATMを使っている本人の手を使って、犯人がボタンを押させる。
- XSS: ATMの画面に犯人のスクリプトを表示させて、情報を盗み取る。
つまり、CSRFはユーザーの「信用」を利用し、XSSはブラウザの「脆弱性」を突きます。どちらも被害は大きいですが、対策のアプローチが異なります。
Spring Securityでは、CSRF対策としてCSRFトークンの付与、XSS対策として出力時のエスケープ処理などを取り入れることが基本です。
4. Spring SecurityではどのようにCSRF対策がされているのか(自動で有効かどうかも含めて)
Spring Securityは、初期設定でCSRF対策が有効になっており、特に何もしなくてもPOSTやPUTなどの状態を変更するリクエストにはCSRFトークンの確認処理が自動で組み込まれます。
具体的には、以下のようなリクエストが対象になります。
- POST(フォーム送信)
- PUT(更新処理)
- DELETE(削除処理)
- PATCH(部分更新)
これらのリクエストには、正しいCSRFトークンが含まれていないとSpring Security側で403 Forbiddenエラーが返されます。
プロジェクトをPleiadesで作成し、Spring Securityにチェックを入れていれば、以下の設定が内部的に適用されます。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf() // CSRF対策を有効にする(デフォルト)
.and()
.authorizeHttpRequests()
.anyRequest().authenticated();
return http.build();
}
}
このように、明示的に.csrf()を記述しなくても有効ですが、セキュリティ機構を把握するうえで知っておくことが大切です。
また、開発中に一時的にCSRF対策を無効にする場合もありますが、本番環境では必ず有効にしておきましょう。
5. フォーム送信時にCSRFトークンがどう使われるか
Spring Securityでは、HTMLフォームからPOSTリクエストを送る場合、CSRFトークンを含める必要があります。これにより、アプリケーション側はリクエストが正当なものかどうかを判定できます。
たとえば、Thymeleafを使ったフォームであれば、th:actionを使って簡単にトークンを埋め込めます。
<form th:action="@{/submit}" method="post">
<input type="text" name="message"/>
<input type="submit" value="送信"/>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</form>
ここでのポイントは、最後のinput type="hidden"がCSRFトークンの情報であることです。
Spring Securityは、リクエストを受け取った際にこのトークンの正当性をチェックし、一致しない場合はエラーを返して処理を止めます。
このトークンは毎回動的に生成されるため、他のユーザーや攻撃者が同じ値を予測することができません。
つまり、トークンを使うことで「本当にそのユーザーが自分の意思で送信したリクエストかどうか」を確認できるというわけです。
なお、@Controllerを使ってPOSTリクエストを受ける場合、以下のように受け取るメソッドを用意しておけば、CSRFトークンも自動で検証されます。
@Controller
public class MessageController {
@PostMapping("/submit")
public String submitMessage(@RequestParam String message, Model model) {
model.addAttribute("message", message);
return "result";
}
}
このように、Spring Security設定とテンプレートエンジンの連携で、初心者でも安全なCSRF対策を実装できます。
セキュリティは一見難しそうですが、トークンという“鍵”をやりとりして本人確認をしていると考えるとイメージしやすくなります。
6. CSRFトークンをSpring Securityで確認する方法(テンプレートエンジンでの埋め込み)
Spring Securityでは、CSRFトークンをフォーム内に安全に埋め込むための仕組みが整っています。特にThymeleafのようなテンプレートエンジンを使っている場合、HTML側でトークンを埋め込むのも非常に簡単です。
たとえば、以下のようなコードを使えば、Spring SecurityのCSRFトークンが自動的にフォームに含まれます。
<form th:action="@{/post}" method="post">
<input type="text" name="comment"/>
<input type="submit" value="送信"/>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</form>
この_csrfという変数は、Spring SecurityとThymeleafが連携して生成しているもので、フォームを描画した時点でCSRFトークンの情報がテンプレートに渡されるようになっています。
実際には、Spring SecurityのCsrfTokenオブジェクトがリクエストに自動でセットされており、テンプレート側から${_csrf.parameterName}や${_csrf.token}でアクセス可能です。
このようにして、WebアプリケーションのPOSTリクエストが信頼できるものかどうかをトークンの一致によって確認できるため、不正なアクセスを未然に防ぐことが可能になります。
7. 自分でCSRFを無効化する場合の注意点(enable/disableの説明)
Spring Securityでは、CSRF対策がデフォルトで有効になっていますが、開発中やAPIのテスト用途では一時的に無効化したくなることもあります。
その場合、HttpSecurityに対して明示的にcsrf().disable()と記述します。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests()
.anyRequest().permitAll();
return http.build();
}
}
このように記述することで、全てのリクエストでCSRFチェックがスキップされます。
ただし、CSRFを無効にするとセキュリティリスクが高まるため、本番環境でこの設定をそのまま使うのは絶対に避けましょう。開発時の利便性だけで無効化してしまうと、意図せず脆弱性が混入する可能性があります。
特にフォーム送信・ユーザー認証・データ登録・更新・削除といった処理を伴うページでは、必ずCSRF対策を有効にするのが原則です。
CSRF対策は一見手間に見えるかもしれませんが、トークンをテンプレートに埋め込むだけで簡単に導入できるため、初心者でも十分対応可能です。
8. 実際に@Controllerを使ってCSRFトークンの確認を行う簡単な例
ここでは、Spring Boot+@Controller構成で、実際にCSRFトークンがどのように扱われるのかを確認できる簡単な例を紹介します。
まずは、ユーザーがコメントを投稿するページを用意しましょう。
@Controller
public class CommentController {
@GetMapping("/comment")
public String showForm() {
return "commentForm";
}
@PostMapping("/comment")
public String submitComment(@RequestParam String comment, Model model) {
model.addAttribute("submitted", comment);
return "result";
}
}
上記の@Controllerでは、GETリクエストでフォームを表示し、POSTリクエストでユーザーのコメントを受け取ります。
続いて、Thymeleafテンプレート側(commentForm.html)では、次のようにフォームを作成します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>コメント投稿</title>
</head>
<body>
<h2>コメントを投稿してください</h2>
<form th:action="@{/comment}" method="post">
<input type="text" name="comment" placeholder="コメントを入力" />
<input type="submit" value="送信" />
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</form>
</body>
</html>
このテンプレートでは、CSRFトークンをinput type="hidden"で埋め込んでいるため、Spring Security側でトークンの正当性が検証されるようになっています。
フォームが送信されると、POSTリクエストにトークンが含まれ、それがSpring Securityで照合されて一致すれば、コメントが正常に処理されます。
処理結果はresult.htmlテンプレートで表示できます。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>投稿結果</title>
</head>
<body>
<h2>投稿ありがとうございます!</h2>
<p th:text="'コメント内容:' + ${submitted}"></p>
</body>
</html>
このように、@Controller構成でも簡単にCSRFトークンの確認処理ができるようになっており、初心者でもSpring Securityのセキュリティ対策を自然に取り入れることができます。
テンプレートにトークンを入れておくだけで、フォーム送信の信頼性を高めることができるため、セキュアなWebアプリ開発の基本として必ず身につけておきましょう。