Rails の protect_from_forgery と Ajax と複数のフォーム

Rails の protect_from_forgery を使う際に、Ajax で複数個のフォーム HTML を取得する場合について。

  • Rails 3.0.4 以上
  • Ajax リクエストに対するレスポンスに含まれるフォームは一つだけ

protect_from_forgery は、簡単に言えば、リクエストに含まれるトークンとセッションに格納されたトークンを比較して、一致してなければ不正リクエストとして処理する仕組みです。
このCSRF対策用のトークンは、session[:_csrf_token] にない場合にのみ代入されます。

つまり、まだ session[:_csrf_token] が空である状況で、Ajax リクエストで複数個のフォーム HTML を取得するページにアクセスすると、以下の状況が発生し得ます。

  1. ページにアクセス
  2. ページ内の Ajax リクエスト(1)がフォーム HTML(1) をリクエストする
    1. session[:_csrf_token] にトークン(1)を代入する
    2. フォーム内にトークン(1)を埋め込む
  3. ページ内の Ajax リクエスト(2)がフォーム HTML(2) をリクエストする
    1. session[:_csrf_token] にトークン(2)を代入する
    2. フォーム内にトークン(2)を埋め込む

この順の通りに処理が進んだとして、フォーム(1)を使って POST リクエストを送信した場合、どうなるでしょうか?
session[:_csrf_token] にはトークン(2)がセットされているため、トークン(1)を含むリクエストは不正と判断されセッションがリセットされます。

と、まあ、まさしくこういう状況に遭遇したわけですが、どうするのがベストの選択だったのでしょうか。ページの構成を見直すべきなのか。思い切って protect_from_forgery を XHR リクエストの場合に無効にしてしまうか(ふさがれた穴を広げる)。

取り急ぎ、Ajax リクエストを起動するページのレスポンスを返す前に form_authenticity_token を実行して session[:_csrf_token] にトークンを入れてしまうという対処をしてみました。
(一度作られたトークンをセッションが破棄されるまで使い続けるという protect_from_forgery の実装をよしとするなら、これもまたよしとされるだろうという発想ですが、本当にトークンが更新されないのかは自信がないです…)