Facebook Twitter Google Maps RSS
Home webサイト CakePHPでSecurityコンポーネント+AjaxでPOST送信
formats

CakePHPでSecurityコンポーネント+AjaxでPOST送信

こんにちは、陽気なシステム屋です。

CakePHPでSecurityコンポーネントを使用しつつAjaxをPOST送信すると、
セキュリティチェックエラーになってしまう。
その解決策を考えてみたので書いておこうと思います。

検証環境は PHP 5.5.24、CakePHP 2.4.7 です。

まず、Securityコンポーネントのトークン生成とチェックの流れはこんな感じ。

▼ページ描画時

トークンを生成、セッションに格納。(トークンはランダム値をキーに、有効期限を値にした配列)
トークンキーをリクエストデータに格納、フォームヘルパーによってフォーム内にhiddenで描画される。

↓こんな感じで。

<input type=”hidden” name=”data[_Token][key]” value=”6ab2d0043ab0a0d7974324ccefe17806cb1279d5″ id=”Token1858912179″>

▼リクエスト送信時

POST/PUT送信時、セッション内にリクエストデータのキーに該当するトークンが存在するかチェック。
チェック終了とともにセッションから該当トークンを削除。(ワンタイムトークン設定=trueの場合)

で、Ajaxで普通にPOST送信するとトークンキーを送ってないので上記チェックではじかれてしまう。
解決策を考えてみます。

①Ajaxアクションでトークンチェックをしない。

AjaxアクションがあるコントローラのbeforeFilterで一時的に設定変更。
一番簡単だけどセキュリティ的には非推奨。

public function beforeFilter() {
 parent::beforeFilter();
 // CSRFチェック、POSTバリデートともOFF
 $this->Security->unlockedActions = array('アクション名');
 // CSRFチェックのみOFFならこちら
 if ($this->params['action'] == 'アクション名') {
   $this->Security->csrfCheck = false;
 }
}
②Ajaxでトークンキーも同時にPOST送信してセキュリティチェックを行う。

さらに新しく発行されたトークンキーを受け取ってJSでフォームに反映する。ちょっと面倒。

// クライアント側
$.ajax({
 url: '/xxx/ajaxHoge',
 type: 'post',
 dataType: 'json',
 data: {
  'data[_Token][key]': $('#targetForm').find('input:hidden[name="data[_Token][key]"]').val()
  'data[xxxxx]': $('#xxxxx').val(), // 必要な値を送る
 },
}).always(function(data){
 // トークンキー書き換え
 if (typeof data.token !== 'undefined') {
  $('#targetForm').find('input:hidden[name="data[_Token][key]"]').val(data.token.key);
 }
});

// フォームデータを丸ごと送信してもいい。
$.ajax({
 data: new FormData($('#targetForm').get(0)),
 ・・・・
});

// サーバ側
public function ajaxHoge() {
 $result = array();
 // ~~ 必要な処理をする ~~
 if (!empty($this->request->params['_Token'])) {
  $result = array('token' => $this->request->params['_Token']);
 }
 echo json_encode($result);
}
③ワンタイムトークンをfalseにしておき、Ajaxでトークンキーも同時にPOST送信してチェックする。

トークンキーを変更しないのでフォーム値の書き換えが不要。でも安全性は下がる。
※POST送信するところは②と同じ。

public function beforeFilter() {
 parent::beforeFilter();
 // ワンタイムトークンをOFF
 $this->Security->csrfUseOnce = false;
}

ちゃんとチェックしているということで、なんだか面倒ですが②を使っております。
もっといい解決策があったら知りたいですね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


*