Component:
<?PHP
namespace Component;
/**
* This component handles all Cross-Site Request Forgery (CSRF) operations
*
* This class behaves very similar to the built-in-Phalcon Security component.
* The difference is that this component only generates the token & key once per session,
* thereby allowing it to be invoked multiple times in a single page, and be used on multiple,
* concurrent pages.
*
*/
class CSRF extends \Phalcon\Mvc\User\Component{
/**
* Make sure any POST requests contain a valid CSRF key & token
*
* This method called by the Dispatcher's Event manager because this component
* was added as a dispatch listener in bootstrap.php.
*
* Forwards (not redirect) to index/csrf if the token wasn't set.
*
* @param $Event The Event causing the method to be triggered
* @param $Dispatcher The Event Dispatcher
*/
public function beforeDispatch(\Phalcon\Events\Event $Event,\Phalcon\Mvc\Dispatcher $Dispatcher){
# Handle CSRF check
if($Dispatcher->getControllerName() != 'index' && $Dispatcher->getActionName() != 'csrf'){
if($this->request->isPost()){
if(!$this->checkToken()){
if($this->request->isAjax()){
echo 'The submitted information did not include a CSRF token, which is required to ensure you actually meant to submit the form.';
exit();
}
else{
$Dispatcher->forward(['controller'=>'index','action'=>'csrf']);
}
}
}
}
}
/**
* Get the CSRF token key
*
* Generates the key & token if key wasn't already set
*
* @see self::generateToken()
*
* @return string The token key
*/
public function getTokenKey(): string {
if(!$this->session->get('csrf_token_key'))
$this->generateToken();
return $this->session->get('csrf_token_key');
}
/**
* Get the CSRF token
*
* Generates the key & token if token wasn't already set
*
* @see self::generateToken()
*
* @return string The token
*/
public function getToken(): string {
if(!$this->session->get('csrf_token'))
$this->generateToken();
return $this->session->get('csrf_token');
}
/**
* Checks $_POST to ensure the proper token key & token were POSTed
*
* @return boolean whether or not the appropriate values were found it $_POST
*/
public function checkToken(): bool{
$stored_key = $this->getTokenKey();
$stored_token = $this->getToken();
$passed_token = $this->request->getPost($stored_key);
if($stored_token == $passed_token)
return TRUE;
else
return FALSE;
}
/**
* Generates the token & key and stores them in session
*/
private function generateToken(){
$this->session->set('csrf_token_key',$this->security->getTokenKey());
$this->session->set('csrf_token',$this->security->getToken());
}
}
Then, in my bootstrap.php
file, I add the component as an event listener:
$DI->set('dispatcher',function(){
// Create an events manager that checks authentication,
// and propagates user information before dispatching
$EM = new \Phalcon\Events\Manager();
// Check any POSTed form had CSRF properly set
$EM->attach('dispatch:beforeDispatch',new \Component\CSRF());
$Dispatcher = new \Phalcon\Mvc\Dispatcher();
$Dispatcher->setEventsManager($EM);
return $Dispatcher;
});