AJAXリクエストでもCSRF対策のトークンを自動送信する方法


前回の続きで、「request_forgery_protection」の問題点について。

  1. GETリクエストをスルーしているため根本的にCSRF対策になっていない
  2. 宣言したアクションだけにしか適用されないので開発者の宣言漏れの可能性がつきまとう
  3. FORMタグを使う部分は問題ないが、JavaScriptAJAXリクエストを発行する時は自前でトークンを付け加えてあげないといけない

2に関しては、所謂ブラックリスト方式かホワイトリスト方式かといった問題。
FWとして「CSRF対策済」と謳う以上はホワイトリスト形式にすべきなのではないかと思う。

3は結構ネット上でも対応方法を記載されている方がちらほら見受けられる。
具体的にどのような場面で問題になるかというと、

  • JavaScriptで直接 new Ajax.Updater() や new Ajax.Requext()を実行
  • InPlaceEditorを利用
  • AutoCompleteを利用


これらの問題に対してどのような対処がされているのだろうか。

  • parametersにrequest_forgery_protection_tokenを自力で混ぜ込む
  • リクエストメソッドをGETに変更する
  • 該当リクエストを受けるコントローラで「skip_before_filter :verify_authenticity_token」する

パラメータに自力でトークンを混ぜ込むのが正しい対処方法ではあるが、開発者にとってはめんどくさいと感じる人も多いんじゃないかな。というか俺はそんなのしたくない。

GETに変更する方法や、before_filterをスキップする方法はセキュリティレベルを下げるだけに過ぎない。その処理がCSRF攻撃されても影響が少ないような大したことのない機能であると認識して実施するのであればまぁ問題ないが、そのような記述無しにBlog等で公開するのは如何なもんかと思う。

では、どうするのが良いか。acculo.usのAJAX通信やprototypeのAjax.Updaterは全てAjax.Requestに集約されている。そして、Ajax.RequestはAjax.Baseを継承している。なので、Ajax.Baseのデフォルト値をごにょごにょしてあげれば全てのAjax通信のパラメータをいじることができる。具体的には以下の通り。

<%= javascript_include_tag 'prototype' %>
<script type="text/javascript">
  Object.extend(Ajax.Base.prototype, {
    initialize_original: Ajax.Base.prototype.initialize,
    initialize: function(options) {
      this.initialize_original(options);
      Object.extend(this.options.parameters, {'<%= request_forgery_protection_token %>':'<%= escape_javascript(form_authenticity_token) %>'});
    }
  });
</script>

これで、全てのAJAXリクエストのパラメータに自動的にトークンが付与されることになる。