How Yii Validate CSRF Token ¶
First of all, You must change component config to enable the default Yii CSRF validation.
'components' => array(
'request' => array(
'enableCsrfValidation' => true,
),
),
Note: When you ebable CSRF validation and use form builder to generate a form(only post), Yii will auto generate a hidden field and put it in the form, at the same time, Yii will create a cookie with CSRF token. When you submit the form, Yii will compare two CSRF tokens from post and cookie.
What Problem Will Happen ¶
1.The user client DOES NOT accept cookie.
2.The user client CAN NOT send a request with cookie.
For example: upload file use flash (swfupload)
How To Solve ¶
A good solution is to use session instead of cookie.
Do it like this
1.First, You must use your own HttpRequest class instead of Yii built-in
Create a new class file HttpRequest extends CHttpRequest in path/to/protected/components
We need override two methods
private $_csrfToken;
public function getCsrfToken()
{
if($this->_csrfToken===null)
{
$session = Yii::app()->session;
$csrfToken=$session->itemAt($this->csrfTokenName);
if($csrfToken===null)
{
$csrfToken = sha1(uniqid(mt_rand(),true));
$session->add($this->csrfTokenName, $csrfToken);
}
$this->_csrfToken = $csrfToken;
}
return $this->_csrfToken;
}
public function validateCsrfToken($event)
{
if($this->getIsPostRequest())
{
// only validate POST requests
$session=Yii::app()->session;
if($session->contains($this->csrfTokenName) && isset($_POST[$this->csrfTokenName]))
{
$tokenFromSession=$session->itemAt($this->csrfTokenName);
$tokenFromPost=$_POST[$this->csrfTokenName];
$valid=$tokenFromSession===$tokenFromPost;
}
else
$valid=false;
if(!$valid)
throw new CHttpException(400,Yii::t('yii','The CSRF token could not be verified.'));
}
}
2.Change the component config to use the HttpRequest class
'components' => array(
'request' => array(
'class' => 'application.components.HttpRequest',
'enableCsrfValidation' => true,
),
),
Some Tips ¶
If user does nothing too long time, the session will be removed by session gc. In that case, CSRF validation will raise a 400 HTTP exception.
The default session timeout in php5 is 1440(may be not exact), your can use function ini_get('session.gc_maxlifetime') to view the default timeout and use the function ini_set('session.gc_maxlifetime', $timeout) to set.
In Yii, We hava a easy way to set. Just change the session component config.
'components' => array(
'session' => array(
'timeout' => 86400,
),
),
It, does not work
I have get error
Fatal error: Call to undefined method CHttpRequest::init() in E:\xampp\yii-framework\framework\base\CModule.php on line 387
Please help me
Yes, It does work.
@16972 probably made a mistake in the name declaration of the class. You simply have to declare the new class like this:
class HttpRequest extends CHttpRequest { // here the code from the post. }
I'm testing it and it works prefectly.
new token on each render
Thanks for this article.
I commeted some code out. I think that it's more secure to get s new token each time you render the page. seems to work fine so far.
public function getCsrfToken() { Yii::log("getCsrfToken is running", CLogger::LEVEL_INFO, __METHOD__); Yii::log("getCsrfToken name: " . $this->csrfTokenName, CLogger::LEVEL_INFO, __METHOD__); if($this->_csrfToken===null) { $session = Yii::app()->session; //$csrfToken=$session->itemAt($this->csrfTokenName); //if($csrfToken===null) //{ $csrfToken = sha1(uniqid(mt_rand(),true)); $session->add($this->csrfTokenName, $csrfToken); //} $this->_csrfToken = $csrfToken; }else{ Yii::log("token is not null", CLogger::LEVEL_INFO, __METHOD__); } return $this->_csrfToken; }
How do you disable this for an API?
class QuestionController extends ActiveController
{
public $modelClass = 'common\models\Question'; //here is code Public $enableCsrfValidation = false;
It working for me
Using postman i tried disabling CSRF for a specific action only
public function beforeAction($action) { if ($action->id == 'notification') { $this->enableCsrfValidation = false; } return true; }
But i keep getting a 400 error. the url works fine when i open it the browser. Just can't post using postman. Any ideas?
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.