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?
I want to use it CDBHttpSession, strange enough, the httprequest class created above, doesn't use the cdbhttpsession if configured the following way.
`
'session' => array(
'class' => 'CDbHttpSession', 'cookieParams' => array( 'domain' => $_SERVER['HTTP_HOST'], //'path' => '/; samesite=Strict', 'secure' => true, 'httponly' => true ), 'connectionID' => 'db', 'autoCreateSessionTable' => false, ),
It just throws 400 bad request Error Error Code:400 The CSRF token could not be verified. No sure how to make the above class access the cdbhttpsession
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.