Codeigniter CSRF valid for only one time ajax request

41,207

Solution 1

In my opinion you should try to recreate your csrf token each request

Try this code example...

For the js funcion

var csrfName = '<?php echo $this->security->get_csrf_token_name(); ?>',
    csrfHash = '<?php echo $this->security->get_csrf_hash(); ?>';
("#avatar").change(function(){
    var link = $("#avatar").val();

    var dataJson = { [csrfName]: csrfHash, id: "hello", link: link };

    $.ajax({
        url : "<?php echo base_url('main/test'); ?>",
        type: 'post',
        data: dataJson,            
        success : function(data)
        {   
            csrfName = data.csrfName;
            csrfHash = data.csrfHash;
            alert(data.message);
        }  
    });
});

and for the controller

public function test() { 
    $config['upload_path'] = './uploads/'; 
    $config['allowed_types'] = 'gif|jpg|png'; 
    $config['max_size'] = 500; 
    $config['max_width'] = 260; 
    $config['max_height'] = 260; 

    $reponse = array(
                'csrfName' => $this->security->get_csrf_token_name(),
                'csrfHash' => $this->security->get_csrf_hash()
                )

    $this->load->library('upload', $config); 
    if (!$this->upload->do_upload('link')) { 
        $reponse['message'] = "error"; 
    } 
    else { 
        $data = array('upload_data' => $this->upload->data()); 
        $image_name = $data['upload_data']['file_name']; 
        $reponse['message'] = $image_name; 
    } 

    echo json_encode($reponse);
}

Let me know and good luck

Note: When someone ask you for posting more data to the question, don't post it as a comment or answer, it's better to edit the question itself and adding the stuff

Solution 2

You can set this in config.php

$config['csrf_regenerate'] = FALSE;

so the csrf protection is valid during all the session time it will solve your problem. If you set $config['csrf_regenerate'] = true; then CI generate new csrf token every request so your old csrf token not match with new generated csrf token

Solution 3

All you need to do is reload the CSRF token in your AJAX response. It is that simple!.

Solution 4

Each time you make a request, the csrf_token is being updated by CI. That's why the CSRF only work once. So everytime we make a request we need to update the csrf_token too. I solve this problem by doing this.

Conroller: get the updated csrf using this code.

public function update_csrf()
{
  $data['csrf_hash'] = $this->security->get_csrf_hash();
  echo json_encode($data);
}

AJAX: replace the old value of your csrf name="csrf_token_name"

var jqXHR = $.ajax({
            url: $(this).attr('action'),
            type: 'POST',
            data: $(this).serialize(),
            dataType: 'json',
        })
        jqXHR.done(function(response) {
            $('input[name=csrf_token_name]').val(response.csrf_hash); //update the csrf to the form 
        })
        jqXHR.fail(function(jqXHR, textStatus, errorThrown) {
            console.log(jqXHR);
            console.log(textStatus);
            console.log(errorThrown);
        });

Important!: use dataType: 'json'

So now each time you have a successful request, the csrf_token is updated too and you are now free from 403 (Forbidden) error.

Solution 5

$config['csrf_regenerate'] = TRUE;

keep auto generate to true it will be more safer. In similar case when csrf is expired in first request. What i have implemented

$(document).ajaxComplete(function (event, xhr, settings) {
 let response = xhr.responseText,
 let obj = JSON.parse(response),
 let csrfData = obj.csrf;
 document.querySelector('input[name="' + csrfData.name + '"]').value = csrfData.hash; 
}); //Also here you can update any other non input element    

In every ajax response we are passing csrf data in which latest csrf data will be replaced with current one

Sample response from request

{ 
csrf : {
  name : 'csrf_name',
  hash : 'qw76sd7s6f78sdfs8dfs9df8cx9'
 }
}

I update csrf token in every ajax request. Also don't choose this method if you are working with multi tab environment

Share:
41,207
Admin
Author by

Admin

Updated on January 07, 2022

Comments

  • Admin
    Admin over 2 years

    I want to upload image on the server on change event of jQuery but using codeigniter csrf I am able to upload image only one time. How can I upload images using ajax for multiple requests.Please keep in mind when I set this

    config['csrf_protection'] = FALSE;
    

    then I am able to send multiple request jQuery onchange event but when csrf_protection is going to be false then I think there is no advantage of csrf. so the question is how can I send multiple requests using ajax while csrf_protection is enable. My jquery code is following

    $("#avatar").change(function(){
        var link = $("#avatar").val();     
        $.ajax({
            url : "<?php echo base_url('main/test'); ?>",
            type: 'post',
            data: {'<?php echo $this->security->get_csrf_token_name(); ?>':'<?php echo $this->security->get_csrf_hash(); ?>',"id":"hello","link":link},            
            success : function(data)
            {   
                alert(data);
            }  
        });
    });
    

    My controller:

    public function test()
    {
        $config['upload_path'] = './uploads/';
        $config['allowed_types'] = 'gif|jpg|png';
        $config['max_size'] = 500;
        $config['max_width'] = 260;
        $config['max_height'] = 260;
        $this->load->library('upload', $config);
        if (!$this->upload->do_upload('link')) {
            echo "error";
        } else {
            $data = array('upload_data' => $this->upload->data());
            $image_name = $data['upload_data']['file_name'];
            echo $image_name;
        }
    }
    
  • Marcelo Agimóvel
    Marcelo Agimóvel over 6 years
    I searched a lot and can tell that use this approach is not recomended, it will cause loss of security, there are other methods.
  • Eloise85
    Eloise85 over 6 years
    I agree Marcelo CSRF protection is necessary on public web apps.
  • Marcelo Agimóvel
    Marcelo Agimóvel over 6 years
    I find out that always regenerate csrf token is not the best way and the problems it bring's to multiple tabs (example) may break your app, so there are other ways. It's a shame CI doesn't have any solution to that, it really makes me sad and I'm leaving code igniter (for other reasons as well).
  • Eloise85
    Eloise85 over 6 years
    I remember I had the same trouble but I found a workaround, I think you should post this question on CI's forum. For me CI stay a good framework for medium/small sites and I think CI 4 will change soon, it's a good open source project maintained by an non profit team.
  • Eloise85
    Eloise85 over 6 years
    All you need is that your ajax controler's response is in json format and in your json you can pass the new csrf token, your frontend parse the json and replace the scrf value in your form's hidden input. Is that clear enough ?
  • semihcosu
    semihcosu almost 6 years
    Please note; although this answer could be great for APIs, it's not a good practice for ajax -or other post- requests. Disabling CSRF may cause security problems.
  • user752746
    user752746 about 5 years
    Hi Edu, any suggestions on how I can go about recreate your csrf token each request? Thanks in advance for your insights.