Yii Framework Forum: Disable CSRF token validation for certain paths - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Disable CSRF token validation for certain paths Rate Topic: -----

#1 User is offline   onre 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 5
  • Joined: 18-August 10
  • Location:Helsinki, Finland

Posted 15 December 2010 - 02:44 AM

In my application, I needed to disable CSRF token validation for certain actions. This is a hack I wrote to accomplish said task.

As far as I can tell, CHttpRequest "happens" before CUrlManager, so route information is not available in a proper fashion. Because this application uses certain CUrlManager settings that make URLs look like "controller/action", this works. There probably are nicer ways of doing this, but this might be useful to someone.

One such way would be instantiating a "temporary" CUrlManager for finding out the proper route, but that'd be hack-ish as well.

The component:

<?php

class HttpRequest extends CHttpRequest
{
  public $noCsrfValidationRoutes = array();

  protected function normalizeRequest()
  {
    parent::normalizeRequest();
    $route = implode('/', array_slice(explode('/', Yii::app()->getUrlManager()->parseUrl($this)), 0, 2));

    if($this->enableCsrfValidation && array_search($route, $this->noCsrfValidationRoutes) !== false)
      Yii::app()->detachEventHandler('onbeginRequest',array($this,'validateCsrfToken'));
  }
}

?>

The config snippet

...
                          'request'=>array(
                                           'class'=>'HttpRequest',
                                           'enableCsrfValidation'=>true,
                                           'noCsrfValidationRoutes'=>array('some/route', 'some/otherroute'),
                                           'enableCookieValidation'=>true,
                                           ),

...

0

#2 User is offline   iGrog 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 64
  • Joined: 09-October 09

Posted 18 December 2010 - 12:30 PM

It's better to do check in validateCsrfToken function only on POST requests, not at all requests. Like this:

public function validateCsrfToken($event)
{
        // only validate POST requests
        if ($this->getIsPostRequest())
        {
            $route = Yii::app()->urlManager->parseUrl(Yii::app()->getRequest());
            if(in_array($route, $this->dontCheckCsrf)) return true;
...


2

#3 User is offline   dead_eye 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 1
  • Joined: 27-July 11

Posted 27 July 2011 - 01:50 PM

I've added a check for *, for those multiple paths, ie "myPath/*", instead of "myPath/1", "myPath/2", etc.

class HttpRequest extends CHttpRequest {
    public $noCsrfValidationRoutes = array();

    protected function normalizeRequest() {

        parent::normalizeRequest();
        if ($this->getIsPostRequest()) {
            if($this->enableCsrfValidation &&  $this->checkPaths() !== false)
                Yii::app()->detachEventHandler('onbeginRequest',array($this,'validateCsrfToken'));
        }
    }

    private function checkPaths() {

        foreach ($this->noCsrfValidationRoutes as $checkPath) {
            // allows * in check path
            if(strstr($checkPath, "*")) {
                $pos = strpos($checkPath, "*");
                $checkPath = substr($checkPath, 0, $pos);
                if(strstr($this->pathInfo, $checkPath)) {
                    return true;
                }
            } else {
                if($this->pathInfo == $checkPath) {
                    return true;
                }
            }
        }
        return false;
    }
}

0

#4 User is offline   befi 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 25
  • Joined: 10-March 11

Posted 09 March 2012 - 01:27 PM

works like a charm! Exactly what I was looking for :)
0

#5 User is offline   ToolMayNARD 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 31
  • Joined: 02-August 10

Posted 15 June 2012 - 11:13 AM

Hello,

I tried with this solutions, but it doesn't work for me. I always get CSRF token could not be verified http 400 error.

my main.php configuration file contains following lines:

// Cross-site Request Forgery Prevention
'request'=>array(
'class'=>'HttpRequest',
'enableCsrfValidation'=>true,
'enableCookieValidation'=>true,
'noCsrfValidationRoutes'=>array('shoppingkart/ccpaymentresponse',),
),

The ccpaymentresponse action is called by external application, so no CSRF token could be sent.

Bye!

Mauro
0

#6 User is offline   yugene 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 513
  • Joined: 08-August 09

Posted 15 June 2012 - 08:44 PM

Hi Mauro,
Which code do you use in HttpRequest?
0

#7 User is offline   ToolMayNARD 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 31
  • Joined: 02-August 10

Posted 16 June 2012 - 03:29 AM

The one posted above by dead_eye. Class saved in /protected/components folder.
The class is called correctly (tested with some echo placed here and there).
0

#8 User is offline   yugene 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 513
  • Joined: 08-August 09

Posted 17 June 2012 - 12:13 AM

Sorry for the late response :)
I looked through the code above and modified/optimised it a bit, so you can try it out:


<?php

class HttpRequest extends CHttpRequest {

    public $noCsrfValidationRoutes = array();

	/**
 	* Normalizes the request data.
 	* This method strips off slashes in request data if get_magic_quotes_gpc() returns true.
 	* It also performs CSRF validation if {@link enableCsrfValidation} is true.
 	*/
	protected function normalizeRequest()
	{
            parent::normalizeRequest();
            if ($this->getIsPostRequest() && $this->enableCsrfValidation && !$this->checkCurrentRoute())
                    Yii::app()->detachEventHandler('onbeginRequest', array($this, 'validateCsrfToken'));
        }
        
        /**
 		* Checks if current route should be validated by validateCsrfToken()
 		* @return boolean true if current route should be validated 
 		*/
        private function checkCurrentRoute() {
            foreach ($this->noCsrfValidationRoutes as $checkPath) 
            {
                if (($pos = strpos($checkPath, "*")) !== false) 
                {
                    $checkPath = substr($checkPath, 0, $pos - 1);
                    if (strpos($this->pathInfo, $checkPath) == 0)
                        return false;
                } elseif ($this->pathInfo === $checkPath)
                    return false;
            }
            return true;
        }
}

Also, as an option, you may overwrite validateCsrfToken($event) instead of normalizeRequest() and put $this->checkCurrentRoute() to the first check, e.g.

	public function validateCsrfToken($event)

	{
		if($this->getIsPostRequest() && $this->checkCurrentRoute())
		{
         			parental code...
                }
        }


Not sure at the moment what would be faster and if there's any significant difference at all.

Hope it'll help and I didn't miss anything :),
Yuga
3

#9 User is offline   ToolMayNARD 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 31
  • Joined: 02-August 10

Posted 20 June 2012 - 05:03 AM

No way, I still get CSRF token error :(

If I disable CSRF token validation application-wise, POST is ok. I think that the credt card payment gateway that should send me a POST request does someting strange I cannot understand. :unsure:
0

#10 User is offline   yugene 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 513
  • Joined: 08-August 09

Posted 20 June 2012 - 05:35 AM

View PostToolMayNARD, on 20 June 2012 - 05:03 AM, said:

No way, I still get CSRF token error :(

If I disable CSRF token validation application-wise, POST is ok. I think that the credt card payment gateway that should send me a POST request does someting strange I cannot understand. :unsure:

Trivial, but first of all check at which exact path do you get the payment notification and if this is the path pointed in config file.
Then you may Yii::log() which way the request is processed inside HttpRequest to find the problem, or make custom request to the url for debugging.



0

#11 User is offline   ToolMayNARD 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 31
  • Joined: 02-August 10

Posted 20 June 2012 - 09:26 AM

Ah, problem solved.

The noCsrfValidationRoutes configuration array should contain the URL in final form and not in the form of route/action form, so if you have URL Manager components configured to translate request you have to put in the HttpRequest component configuration array in the final form.

In my case I needed simply to change these lines of code:

// Cross-site Request Forgery Prevention
            'request'=>array(
                'class'=>'application.components.HttpRequest',
                'enableCsrfValidation'=>true,
                'enableCookieValidation'=>true,
                'noCsrfValidationRoutes'=>array('shoppingkart/ccpaymentresponse'),
            ),


into:

// Cross-site Request Forgery Prevention
            'request'=>array(
                'class'=>'application.components.HttpRequest',
                'enableCsrfValidation'=>true,
                'enableCookieValidation'=>true,
                'noCsrfValidationRoutes'=>array('shoppingkart/ccpaymentresponse.html'),
            ),


This is not a good solution. It would be better to use a filter for CSRF that works before URL manager. I will try to make it.

Thank you Yuga for you help!

Mauro
0

#12 User is offline   yugene 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 513
  • Joined: 08-August 09

Posted 20 June 2012 - 10:23 AM

Welcome.
Nice to hear you solved the issue :)


0

#13 User is offline   sirin k 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 35
  • Joined: 15-March 12
  • Location:India

Posted 01 October 2012 - 01:15 AM

@ToolMayNARD:thank you very much.your solution worked for me.Saved my time.thanks alot.
Sirin k
Yii developer
http://sirink.tumblr.com
0

#14 User is offline   clapas 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 39
  • Joined: 21-February 11

Posted 17 May 2013 - 03:52 AM

Thank you yugene, just a tiny correction. I had to change

if (strpos($this->pathInfo, $checkPath) == 0)


into

if (strpos($this->pathInfo, $checkPath) === 0)


Cheers
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users