CakePHPでCSRF対策
CakePHPでCSRF対策するにはSecurityコンポーネントを使うと簡単にできるらしいけど、色々ぐぐってみるとなかなか一筋縄でいかなそうであるのと、どうもhiddenの値を書き換えるとエラーを返してくれる高機能がもれなくついてくるらしかったので、自前でトークンを作るコンポーネントを導入しました。
といっても下記URLにまさに探してたようなものがあったので、それを参考に作成しました。ほぼまんまですけど。
http://d.hatena.ne.jp/cake67/20091217/1261019930
しかも機能としてはややダウングレードしてる部分もあったり。
とりあえずものすごーく単純に、下記のような仕様に。
- コンポーネントは渡されたトークンをチェックしてtrue/falseを返すだけで、エラー時の処理はaction,controller単位でお任せする。
- ヘルパーはトークンを吐き出すだけ。
使い方は単純に各アクションに
if(!$this->Token->checkToken()){ //error }
っていれるだけ。
viewの方には
$token->create();
と入れるだけの簡単なお仕事です。
ソースは下記のような感じ。
component
<?php /** * CSRF対策用Tokenチェック */ define('GET' , 'get'); define('POST' , 'post'); class TokenComponent extends Object { /** * Components used by TokenHelper * * @var array * @access public */ var $components = array('Session'); var $_form = array(); var $_url = array(); var $_action; var $type; var $useToken = true; function initialize(&$controller) { $this->_action = $controller->action; if(isset($controller->params['form'])){ $this->_form = $controller->params['form']; } if(isset($controller->params['url'])){ $this->_url = $controller->params['url']; } $this->Session->startup($controller); } /* true: Token OK */ function checkToken($method = POST ,$tag_name = '__Token', $hash_type = 'md5') { if ($this->useToken === false) { return true; } $hashed_session_id = $this->get_hashed_session_id($hash_type);//元のソースには入ってなかったけどmd5以外を指定するなら必要だよね? if($this->_form){ if(POST == strtolower($method)){ if(isset($this->_form[$tag_name])){ return $this->_form[$tag_name] == $hashed_session_id; } } else{ if(isset($this->_url[$tag_name])){ return $this->_url[$tag_name] == $hashed_session_id; } } } return false; } /* 現在のセッションIDを暗号化して取得 */ function get_hashed_session_id($hash_type = 'md5') { $session_id = $this->Session->id(null); if (!$session_id) { //$this->_blackHole('No Session.'); return false; } return Security::hash($session_id. Configure::read('Security.salt'), $hash_type); } }
helper
<?php /** * CSRF対策用Token出力ヘルパー * 要Formヘルパー */ class TokenHelper extends AppHelper { /** * Other helpers used by TokenHelper * * @var array * @access public */ var $helpers = array('Form', 'Session'); /* Tokenをセットしたhiddenタグ出力 */ function create($tag_name = '__Token', $id = '__Token' ,$hash_type = 'md5') { $hashed_id = $this-> get_hashed_session_id($hash_type); return '<input type="hidden" name="'.$tag_name.'" value="'.$hashed_id.'" id="'.$id.'" />'; } /* 現在のセッションIDを暗号化して取得 */ function get_hashed_session_id($hash_type = 'md5') { $session_id = $this->Session->id(); return Security::hash($session_id. Configure::read('Security.salt'), $hash_type); } }
Ajaxの際にも使用したかったので、inputタグを吐くところはFormHelperは使用せず普通にべた書きしてます。
まぁAjaxのときでも普通にkeyにdata["__Token"]とかしてやれば、dataに入ってくるのかな?