How to validate CSRF token with session

You are viewing revision #4 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.

« previous (#3)

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