CSRF対策についてどのような設計、実装にすれば良いか悩み中。例えば下記のような感じで実装したとします。
パターン1
// formクラスのインスタンス化。tokenを生成しセッション変数とhiddenに保存。 $form = new form( 'key', $_POST ); // $_POST['token'] と $_SESSION[$key]['token']が一致しているか確認 if ( $form->check() ) { // 処理(エラーチェック、入力・確認・完了画面判定) // 処理完了したら$_SESSION['key']['token']を破棄 }
フォームの入力画面を表示するときにtokenを生成し、セッション変数$_SESSION[$key][‘token’]に保存(トークンを$_SESSION[‘token’]なんかに入れてしまうと複数機能の同時編集ができなくなってしまうため、フォームごとに$keyを設定)。また、同じ値をhiddenフィールドに格納しています。checkメソッドで値が一致するかチェックし、同じなら処理を続け、処理・完了画面表示が完了したらセッション変数を破棄します。
問題点
- 入力画面を表示するたびにセッション変数が生成されるので、処理までいかないで離脱されてしまうとセッション変数が残ってしまう。
- インスタンス化するときの引数がセッションのキーになっているので、キー名を管理しないといけない。
- 処理完了後にそのキー名のセッション変数が破棄されるので、複数タブを開いての同一機能の同時編集ができない。
上記はいわゆるワンタイムトークン方式なので、固定トークンにしたら良いのではと思い、下記のような実装を考えてみました。
パターン2
// トークンはログイン時に生成。ログアウトするまで保持。 if ( $auth->loginCheck() ) { $_SESSION['token'] = 'xxx'; } /* 〜 */ $form = new form( $_POST ); // $_POST['token'] と $_SESSION['token']が一致しているか確認 if ( $form->check() ) { // 処理(エラーチェック、入力・確認・完了画面判定) }
これだとセッション変数の肥大化防止や同時編集は可能です。しかし、「戻る」されたときに問題が。パターン1では処理が完了するとセッション変数が破棄されるので、処理完了後「戻る」されてもhiddenとセッション変数が一致せず、再度処理されることはありません。しかし、パターン2では、セッション変数が破棄されないので処理完了後「戻る」されたときに処理が実行されないような対策が別に必要になります。
問題点
- 「戻る」時の二重送信防止対策が別途必要。
- セッションが持続している間はトークンが固定なので、セキュリティ的にワンタイムトークンより弱い気がする。
では、パターン2で処理完了後にセッション変数を破棄すればどうか
同一機能の同時編集に加え、複数機能の同時編集も不可能になってしまいます。
他に方法がないか適当に思いついたもの
- $_SESSION[‘token’]を配列にしてフォーム表示ごとにユニークなトークンを格納。in_arrayでトークンが存在するかチェック、処理完了後そのフォームのトークンを破棄。⇒セッション変数が無駄に肥大化する。
- ウィンドウ毎にユニークなトークンを発行し$_SESSION[‘token’]配列に格納。⇒ナイスアイディアっぽいけどウィンドウ毎にユニークな識別子の取り方が不明。
パターン1と2を時と場合で使い分けるしか無いのかなぁ。
いわゆる徳丸本購入した。
徳丸本購入、CSRF対策のとこだけつまみ読みしたところ、固定トークンが推奨されていました。同時編集考えたらやはりそれしか無いっぽいもんなぁ。でもそれだと二重送信防止をどのように実現すれば良いか考えないといけないですね…。