How to validate CSRF token with session

  1. How Yii Validate CSRF Token
  2. What Problem Will Happen
  3. How To Solve
  4. Some Tips

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,
    ),
),