Csrf + Ajax + Cavtive Form Submission

Hi

I am using a CSRF token generated by yii and save it to session .

I have overrided the HTTPRequest handler to validate the post token to the session token.


class HttpRequest extends CHttpRequest {

    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];

               echo  $valid=$tokenFromSession===$tokenFromPost;

            }

            else

                $valid=false;

            if(!$valid)

                throw new CHttpException(400,Yii::t('yii','The CSRF token could not be verified.'));

        }

    }

}



and have also add the token to Ajax request as parameter like this…




$('#link').click(function() {

     var token=$('input[name="YII_CSRF_TOKEN"]').val();

      //alert("here");

      //console.log("here");

       $.ajax({

           data:{YII_CSRF_TOKEN:token},

            type: "POST",

            url: "/home/xxxx"

          }).done(function( msg ) {

            console.log("notification  no. reset");

            $("#notification_no").html(msg);

          });

    });



My login form is like this




 <?php

                                $form = $this->beginWidget('CActiveForm', array(

                                    'id' => 'login-form', 'action' => $this->createUrl(Yii::app()->homeUrl . 'user/login'),

                                    'enableAjaxValidation' => true,

                                    'enableClientValidation' => false,

                                    'clientOptions' => array(

                                        'validateOnSubmit' => true,

                                    ),

                                        ));

                                ?>

<?php $model = new LoginForm; ?>


 <?php echo $form->textField($model, 'username', array('autocomplete'=>'off','value' => 'username')); ?>

<?php echo $form->error($model, 'username'); ?>


 <?php echo $form->passwordField($model, 'password', array('autocomplete'=>'off','value' => 'password')); ?>

<?php echo $form->error($model, 'password'); ?>

<?php echo CHtml::submitButton('Login', array('class' => 'loginBtn')); ?>



And the config file settings is like this




'request'=>array(

            'class' => 'application.components.HttpRequest',

            'enableCsrfValidation'=>true,

            'enableCookieValidation'=>true,

        ),



model rules are same as loginform.php model.

CASE 1

when config settings are




'request'=>array(

            'class' => 'application.components.HttpRequest',

            'enableCsrfValidation'=>false,

            'enableCookieValidation'=>true,

        ),



the auto ajax validation on my login form work perfectly and submits the form.

CASE 2




'request'=>array(

            'class' => 'application.components.HttpRequest',

            'enableCsrfValidation'=>true,

            'enableCookieValidation'=>true,

        ),



when i entered wrong username and password i do not get any error notification on my form however i do get a response checked through firebug console.

1{"LoginForm_password":["Incorrect username or password."]}, here 1 means csrf token is validated

when i submit correct username and password i get the following resposne in firebug console

1[], again 1 is for verified csrf token with no error

in both the cases above, my form stops and do not gets submitted. to pass the post action.

so this means that ajax validation is working but error messages are not getting displayed on the form.

second the form is not getting submitted when there is no error.

any help would be greatly appreciated.

Thanks

Now that you know CSRF validation is working, just remove the code that echoes your ‘1’ and you’ll be ok I think :)

Thanks bennouna for your reply.

I remove that echo and my code works.

Hi, I have a similar problem about CSRF and ajax… I have an ajaxLink and a link having some javascript function for the first, and calling a javascript function with the second like :


<?php //PHP DELETE ELEMENTS

  

  echo CHtml::ajaxLink('<img id="deleteImg" src="/myNewApp/images/delete.gif" height="15px" width="15px"/>',

                      $this->createUrl('/element/deleteSelection'), 

                      array('type' => 'post',

                        'beforeSend'=>'function(){

                                                  if($.fn.yiiGridView.getSelection("element-grid") == "") {

                                                    alert("Pleas select record(s)");

                                                    return false;

                                                  } else {

                                                    return true;

                                                  }

                                                }',

                        'data' => array('selection'=>'js:$.fn.yiiGridView.getSelection("element-grid")','YII_CSRF_TOKEN'=>Yii::app()->request->csrfToken),

                        'update' => '#output',

                        'success'=> 'function(){window.location.reload();}'),

                      array('confirm'=>'Are you sure to want to delete these items ?')

                      );

?>   

  

  

<?php //PHP UPDATE ELEMENT

  echo CHtml::link('<img id="updateImg" src="/myNewApp/images/update.gif" height="15px" width="15px"/>',"",array('onclick'=>"{

                                      if($.fn.yiiGridView.getSelection('elment-grid') == '') {

                                        alert('Pleas select a record');

                                        return false;

                                      }

                                      else if($.fn.yiiGridView.getSelection('element-grid').length > 1) {

                                        alert('Only one record can be edited on same time');

                                        return false;

                                      }

                                      else {

                                        updateElement($.fn.yiiGridView.getSelection('element-grid'),'".$this->createUrl('')."','element-grid','#dialogElements','".$csrf."');

                                        $('#dialogElement').dialog('open');

                                        return true;

                                       }}")

                                       );?>

Like you see, for the first ajaxlink in order to delete the lines I select in my grid, I tried to put the csrf in the data but that’s not working… still having csrf not validate

For the second link, I call onclick a javascript function in another file (see below) which is opening a dialog with a update form… but again here I have problem with the csrf.


function updateElement(idvg,url,grid,box_name,csrf)

{

    if (/treemenu\/ajaxFillTree$/.test(url))

    {

      url = url+'/../createNewEntry/view?parentId='+idvg;

    }

    else

    {

      url = url+'/../update/'+idvg;

    }

      jQuery.ajax({'url':url,'data':idvg+'&'+csrf,'type':'post','dataType':'json','success':function(data)

            {

                if (data.status == 'failure')

                {

                    $(box_name+' div.divForForm').html(data.div);

                          // Here is the trick: on submit-> once again this function!

                    $(box_name+' div.divForForm form').submit(updateElement);

                }

                else

                {

                    $(box_name+' div.divForForm').html(data.div);

                    setTimeout("$('"+box_name+"').dialog('close') ",1000);

                    window.location.reload();

                    //$.fn.yiiGridView.update(grid);

                }

 

            } ,'cache':false});;

    return false;

}

Have you some ideas to get it working ?

all works perfectly before I include csrf ;)

thanks

Well, after checked, only the second, UPDATE ELEMENT, doesn’t work, the first one is working

In this line


updateElement($.fn.yiiGridView.getSelection('element-grid'),'".$this->createUrl('')."','element-grid','#dialogElements','".$csrf."');

What is $csrf? It appears nowhere else. Shouldn’t it be


updateElement($.fn.yiiGridView.getSelection('element-grid'),'".$this->createUrl('')."','element-grid','#dialogElements','" . Yii::app()->request->csrfToken . "');

?

Ok I’ve just seen your updateElement js function, and you’ve been missing also the token name.


updateElement($.fn.yiiGridView.getSelection('element-grid'), '" . $this->createUrl('') . "', 'element-grid', '#dialogElements', 'YII_CSRF_TOKEN=" . Yii::app()->request->csrfToken . "');

ho yep sorry, I didn’t write it in my reply but :

$csrf=Yii::app()->request->csrfTokenName.’=’.Yii::app()->request->csrfToken;

I added an alert() in my js function like : “alert(‘idvg=’+idvg);”

the first time I click on my link the alert gives me well the id of the row I selected. Afterwards, I have my form in my dialog like usually. But when I click on "save" button below my form, a new alert() appears but this time with idvg=[Object Object]… this is very weird… And I think the cause is here. Indeed with firebug, when I "POST" I have at the end of url thing like "%5Object%20Object" meaning nothings… so 400 bad request error.

I don’t really understand why introduce csrf could put changes like this on my variable huu…

Solved : I simply change


$(box_name+' div.divForForm form').submit(updateElement);

by


$(box_name+' div.divForForm form').submit(nothing);

I didn’t think further why it didn’t work =P