二重送信について
二重送信が必要な理由については調べてください。
ざっくり言うとボタンとリクエストが1:1である中で、複数回実行されると困るリクエストに対する対策として必要なものです。
一般的な対応方法
主に以下の記事が参考になります(Macchinettaさんでも大丈夫だと思います)。
記事の概要を言うと、以下の3つの対策方法があります。
-
JavaScriptによる2回以上ボタンを押させなくさせる機構
ボタンクリックをトリガにしてボタンにdisabled属性などをつける。これによりボタンは押せないのでリクエストを1回に強制させることができる。ただHTML系は開発者ツールで強制突破することができ、スクリプトで実行されると画面からのリクエストではないため、強制することはできない。 -
PRG(Post-Redirect-Get)パターンを利用した機構
必ずPOSTでリクエストを受けるようにし、内部でリダイレクトさせて処理を行うようにする。これによりブラウザバック時のリクエストを通さないようにすることができる(ブラウザでの検索はGETであるため通らない)。ただこれでもスクリプトでPOSTリクエストを投げられると防止することができない。 -
トランザクショントークンチェックを使用した機構
今回の記事の内容。リクエストの内容にトランザクショントークン(以降トークン)を入れることで、アプリ側に1回目のリクエストかどうかをチェックさせる。考え方としては画面に対して1つのトークンを設けることで、そのトークン以外からのリクエストを防止し、処理中はそのトークン以外の処理を受け付けないようにすることができる。
トランザクショントークンチェックの実装
自分で実装もできますが、ここはライブラリを使用しましょう。提供されているライブラリは以下の2種類です。
- org.terasoluna.gfw:terasoluna-gfw-web:5.9.0.RELEASE
https://github.com/terasolunaorg/terasoluna-gfw - jp.fintan.keel:keel-spring-boot-starter-web:2.0.0
https://github.com/Fintan-contents/keel-spring-enhance
※バージョンは逐一変更してください。
どちらでも良いのですが、私が判断した基準は以下の2点です。
- GradleLintを使用しているかどうか
- CSRFトークンチェックを使用するかどうか
GradleLintを使用している場合は2が良いです。これは1だと内部でaspectjライブラリのクラス名が重複し、警告ログが出るようになるからです。もちろん設定で除外はできますが、ライブラリを入れることで入れる側がライブラリについての制御を行うのは依存関係が広がってしまうのでやめておきたいところです。
CSRFトークンチェックをする場合は1が良いです。これは2だとRequestDataValueProcessorが干渉した際、対応する方法が少し難しくなるためです。1だとライブラリにそれを実現させる機構があるので、そういう意味では楽です。対応方法については、他記事が参考になりますのでそちらをご確認ください。
では実際の実装です。ライブラリとしては2を採用しています。
@Controller
@TransactionTokenCheck("signup")
public class SignupController {
@PostMapping("/signup")
@TransactionTokenCheck(type = TransactionTokenType.BEGIN)
public String index() {
return "/signup";
}
@PostMapping(path = "/signup", param = "regist")
@TransactionTokenCheck
public String regist() {
return "/signup"
}
}
classに注入した@TransactionTokenCheckはそのControllerを使用する画面に対してのトークン名です。
またmethodに注入したものはそのトークンをどこで始めて、どこでチェックするかを決めてます。
上記の実装例で言うとtypeがTransactionTokenType.BEGIN
の場合は、トークンが払い出されるタイミングで、typeのデフォルト(もしくはTransactionTokenType.IN)の場合はトークンをチェックするタイミングになります。
トランザクションの範囲を把握するために同じトークンを管理する場合は同じControllerに定義した方が良いと思います。
まとめ
- Javaでトークンを用いて二重送信を防止するライブラリは2つある
- トークン管理はトークンの発行、チェックの2種類ある
セキュリティ観点や不正操作の防止など、まだまだ勉強させられる毎日です。