トークンで二度押し防止
トークンを使ってフォーム送信時の多重登録の制御
確認
確認時にtokenを発行。
PHP
// 確認トークン発行
// PHP7以降
$_SESSION['token']['confirm'] = bin2hex(random_bytes(32));
// PHP5.3以降
// $_SESSION['token']['confirm'] = bin2hex(openssl_random_pseudo_bytes(32));
$view->assign('token_confirm', $_SESSION['token']['confirm']);
$view->display('confirm.tpl');
hiddenで送信。
Smarty
<input type="hidden" name="token_confirm" value="{$token_confirm}">
完了
PHP
// 完了時全体処理開始
// ---1
$token_confirm = filter_input(INPUT_POST, 'token_confirm');
// 確認トークンが発行されていない、または確認トークンがPOSTされていない
if (empty($_SESSION['token']['confirm']) || empty($token_confirm)) {
// ---2
// エラー処理(入力画面に戻す処理など)
}
// 確認トークンが一致、完了トークンが発行されていない
if (hash_equals($_SESSION['token']['confirm'], $token_confirm) && empty($_SESSION['token']['complete'])) {
try {
// ---3
// 処理(DB登録、メール送信などの処理)
// 処理成功
// 完了トークン発行
$_SESSION['token']['complete'] = bin2hex(random_bytes(32));
} catch (Exception $e) {
// ---4
// 処理失敗(エラー発生のメッセージなど)
}
}
// 完了トークンが発行されていない
if (empty($_SESSION['token']['complete'])) {
// ---5
// エラー処理(入力画面に戻す処理など)
}
// 確認トークンを更新
$_SESSION['token']['confirm'] = bin2hex(random_bytes(32));
// 完了時全体処理完了
// ---6
$view->display('complete.tpl');
正常な処理においては 1 > 3 > 6の順で通過。
確認トークンを更新することで二度押し、リロードやリロードされた場合は、 1 > 6の順となる。
確認トークンが異常な場合は 1 > 2 の順で処理停止。
例外発生した場合は 1 > 4 の順で処理停止。
完了トークンが無効な場合は 1 > 5 の順で処理停止。SESSIONに保存された確認トークンとPOSTされた確認トークンが一致しないと完了トークンが無効となる。
全てのトークンは入力時(修正で戻ったときも同様に)にunsetして初期化する。
二度押し防止対策として確認-完了についての解説になっていますが、CSRF対策でトークンを利用するのであれば、入力画面(必要な箇所があれば全てに)でも制御を施すようにします。