Rails の protect_from_forgery と Ajax と複数のフォーム
Rails の protect_from_forgery を使う際に、Ajax で複数個のフォーム HTML を取得する場合について。
protect_from_forgery は、簡単に言えば、リクエストに含まれるトークンとセッションに格納されたトークンを比較して、一致してなければ不正リクエストとして処理する仕組みです。
このCSRF対策用のトークンは、session[:_csrf_token] にない場合にのみ代入されます。
つまり、まだ session[:_csrf_token] が空である状況で、Ajax リクエストで複数個のフォーム HTML を取得するページにアクセスすると、以下の状況が発生し得ます。
この順の通りに処理が進んだとして、フォーム(1)を使って POST リクエストを送信した場合、どうなるでしょうか?
session[:_csrf_token] にはトークン(2)がセットされているため、トークン(1)を含むリクエストは不正と判断されセッションがリセットされます。
と、まあ、まさしくこういう状況に遭遇したわけですが、どうするのがベストの選択だったのでしょうか。ページの構成を見直すべきなのか。思い切って protect_from_forgery を XHR リクエストの場合に無効にしてしまうか(ふさがれた穴を広げる)。
取り急ぎ、Ajax リクエストを起動するページのレスポンスを返す前に form_authenticity_token を実行して session[:_csrf_token] にトークンを入れてしまうという対処をしてみました。
(一度作られたトークンをセッションが破棄されるまで使い続けるという protect_from_forgery の実装をよしとするなら、これもまたよしとされるだろうという発想ですが、本当にトークンが更新されないのかは自信がないです…)