force.com開発におけるCSRF対策について。

CSRFについて

CSRFとは簡単に言うと、あるサービスにログインした後に、別の全く関係がない罠サイトのリンクやボタンをクリックすることで、ログインしたサービスの予期しない処理が行われてしまうWebサイトの脆弱性です。

要因としては、Webサイト側が予期していない別のWebサイトからのリクエストに対して処理を弾くようなロジックが抜けている為に発生します。

このCSRFを防ぐには

  1. トークンの発行
  2. 処理の前にパスワードの再入力を求める確認画面を挟むようにする
  3. Refererで検知

のような方法が実施され、大抵のWebアプリ場合、Aのトークン発行で対策しているようです。

Force.comにおけるCSRF対策

それでは、force.comにおいてはCSRF対策はどうやって取られているかというと

  • Visualfoceページの<apex:form>タグで生成されたformタグ内にCSRFトークンが埋め込まれる。
  • ActionFunctionやJavaScript Remotingでのajax処理でもCSRFトークンがパラメータに埋め込まれる。

というようにトークン発行・検証によってCSRF対策がされています。

こうすることで、apexタグや標準で用意されているajax処理でのメソッド呼び出しは全てCSRFトークンが埋め込まれるため、標準の機能を適切に利用していれば、安心してforce.comアプリを作成できるということになります。

 

ただし、これは「適切」に利用しているパターンですので、force.comアプリでも自動的にCSRF対策が取られないケースがあります。
以前の記事で、Visualforceページで自前のRESTを実装することでも、ajax処理が実装出来ることを書きました。しかし、これは標準機能の範囲外ですので、force.comが自動的にCSRFトークンを発行してくれません。つまり、CSRF対策を明示的に行わないと、CSRF脆弱性を引き起こします。

 

よって、VF画面からajaxをするのに、自前のREST VFページを利用する際には以下のような仕組みが必要になります。

  1. ユーザにCSRFトークンとトークン発行日、トークン期限日等のカスタム項目を作成
  2. ユーザが対象VFにアクセスしたら、上記項目を更新し、<input type=”hidden”>に自前CSRFトークンを入れる。
    (SessionIDをゴニョゴニョした値とかをトークンに設定すれば、ワンタイムトークンではなく、ログイン中はずっとトークンが有効になる)
  3. REST VFページのApexクラス内でユーザのCSRFトークン値(あるいはSessionIDをごにょごにょした値)と、POSTのCSRFパラメータが同一であるかどうかを検証し、同一であれば処理を実行。
  4. バッチ処理か何かでexpireしたトークンを無効化する。

脆弱性がまだ残ったままの気がしますが、こういうような対策が必須になってきます。

 

VFの自前REST APIのケースでなくても、

対象オブジェクトの詳細画面のカスタムボタンからVFページ呼び出し(パラメータに対象オブジェクトのSFID差し込み)
→呼び出されたVFページでパラメータを読み取り、対象オブジェクトに対してDML等の処理を実行する。

というパターンもそのままだとCSRF脆弱性があり、

  1. VF呼び出しをJavaScriptによる動的form生成からPOST実行に切り替える
  2. 何らかの値をCSRFトークンとして送信。
  3. VF側でPOST値のトークンを検証。

という実装にするか、「埋め込みVFでボタンを見せる」方法にする等、対策を取る必要があります。

後者のほうがVFのformタグを利用する為、対策としては強固で、VFの表現を利用出来る点でおすすめです。カスタムボタンと違って上下にボタンが配置されないこと以外は特にユーザビリティ的なデメリットも無いはずです。ボタンも大きく出来たり画像にできたり自由ですし。

 

ということで、force.comで凝ったことをする場合にはこういった脆弱性に注意しましょー