0
votes

I have a project in Cakephp 3.6 in which 3 actions in MessageController are called by Ajax. I have a problem, however, when I send a request to one of the action, XHR returns to me this:

{
   "message": "CSRF token mismatch.",
   "url": "\/messages\/changepriority\/8",
   "code": 403,
   "file": "D:\\xampp\\htdocs\\myapp\\vendor\\cakephp\\cakephp\\src\\Http\\Middleware\\CsrfProtectionMiddleware.php",
"line": 195
}

This is one of the action what I try to call from Ajax:

public function changepriority($id=null) 
{
    $this->autoRender = false;
    $message = $this->Messages->get($id);
    $message->priority = ($message->priority === false) ? true : false;
    if ($this->Messages->save($message)) {
        echo json_encode($message);
    }
}

And this is my ajax:

$(".email-star").click(function(){
        var idmessage = this.id;
        $.ajax({
        headers : {
              'X-CSRF-Token': $('[name="_csrfToken"]').val()
           },
         dataType: "json",
         type: "POST",
         evalScripts: true,
         async:true,
         url: '<?php echo Router::url(array('controller'=>'Messages','action'=>'changepriority'));?>' +'/'+idmessage,
         success: function(data){
            if(data['priority'] === false) {
                $("#imp_" + idmessage).removeClass("fas").removeClass('full-star').addClass( "far" );
            }
            else {
                $("#imp_" + idmessage).removeClass("far").addClass( "fas" ).addClass("full-star");
            }
          }
         });
    });

I have read the documentation about Cross Site Request Forgery, and I tried to turn off the Csrf for these action first with:

public function beforeFilter(Event $event)
{
     $this->getEventManager()->off($this->Csrf);
}

and then with:

public function beforeFilter(Event $event)
{
     $this->Security->setConfig('unlockedActions', ['index', 'changepriority']);
}

But nothing. The Xhr return always the CSRF token mismatch. What can I do ?

Edit:

I change the action in this way:

    public function changepriority($id=null) 
{
    $this->autoRender = false;
    $message = $this->Messages->get($id);
    $message->priority = ($message->priority === false) ? true : false;
    if ($this->Messages->save($message)) {
        $content = json_encode($message);
        $this->response->getBody()->write($content);
        $this->response = $this->response->withType('json');
        return $this->response;
    }
}

In that way the action works. Can it be like that?

1
On an unrelated note, controlls should never echo data!ndm
The solution you send to me work, but Is there any way to send a csrf parameter to the controller without using a form?l3nox
Yes, see my comment to D3ad L0cKs answer.ndm
Yes, I've seen the comment, but I do not really understand how to do itl3nox
You just need to replace $(...).val() with the <?= json_encode(...); ?> example.ndm

1 Answers

1
votes

First check your $('[name="_csrfToken"]').val() output.

If you didn't get any output, need to check csrfToken hidden field is exist or not. Just right click in your page and click View Page Source

If not exist, you don't follow proper way when you create Form. Basically, when forms are created with the Cake\View\Helper\FormHelper, a hidden field is added containing the CSRF token.

If everything is correct, add the following line inside your ajax call after header

beforeSend: function (xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('[name="_csrfToken"]').val());
},

Ps. Disabling the CSRF is not recommended by cakePHP and most of the developer aware of this. Hope this help.