Yii_Csrf_Token - Multiple Generations On Very Close Page Fetches

Hi

I have some issues with the YII_CSRF_TOKEN which works fine most of the time, but not always in the following cases:

  1. Accessing the same pages from multiple locations;

  2. Multiple requests on initial connect.

In my web application, I have one main page (imagine it as some kind of dashboard) which is updated through AJAX calls. So once the dashboard is loaded, the page stays along quite a while.

In the first case (“1. above”), it seems that this was related to page caching. Valid different users on different IPs managed to get at some point the same YII_CSRF_TOKEN in their “HTML” and a different one in their COOKIE [easy to say, hard to find!]. So I fixed that by adding appropriate “No cache”’ related directives in the request header.

I thought that I fixed the issue entirely, and I ran in to the second one (Issue "2."): a user was generating requests with an invalid YII_CSRF_TOKEN and it turns out that the dashboard page was requested 3 times in the same second (server logs) and that three YII_CSRF_TOKENs were generated for that user in the same second (on the same IP). Which is related to the cookie not being set yet.

I plan on fixing the second issue by keeping the YII_CSRF_TOKEN for three to five seconds in the cache with a key based on the IP and the User-Agent. I may need to add a Mutex for YII_CSRF_TOKEN generation based on the same key.

If you have other suggestions, do not hesitate to mention them here. I am mainly reporting this so that you may thing of a solid solution for this in the framework.

For information, to resolve the second issue, I added this to my implementation which overloads CHttpRequest:




    /**

    * (non-PHPdoc)

    * @see CHttpRequest::createCsrfCookie()

    */

    protected function createCsrfCookie() {

        $key=$this->userHostAddress."#".$this->userAgent;

        $result=Yii::app()->cache->get($key);

        if($result===false) {

            $result=parent::createCsrfCookie();

            Yii::app()->cache->set($key,$result,5);

        }

        return $result;

    }



Tested with successive page reloads of the same page with the YII_CSRF_TOKEN disabled (and some Yii::log calls inserted in the above code). YII_CSRF_TOKEN reuse works.

This the version using the mutex extension:





    /**

 	* (non-PHPdoc)

 	* @see CHttpRequest::createCsrfCookie()

 	*/

    protected function createCsrfCookie() {

        $key=$this->userHostAddress."#".$this->userAgent;

        while(!Yii::app()->mutex->lock($key,2))

        {

            sleep(1);

        }

        try {

            $result=Yii::app()->cache->get($key);

            if($result===false) {

                $result=parent::createCsrfCookie();

                Yii::app()->cache->set($key,$result,5);

            } 

        } catch(Exception $e) {

            Yii::app()->mutex->unlock();

            throw $e;

        }

        Yii::app()->mutex->unlock();

        return $result;

    }