简体   繁体   中英

Refresh CSRF Token on AjaxPOST Datatables: CodeIgniter

I'm trying to make datatables work in my website. But when I click search, next page, sorting, it's not working. This is because of the CSRF token not being regenerated.

Here's my code:

HTML

<input type="hidden" id="hash" name="csrf_test_name" value="802daa2efaf69edb83b571d7bf7510aa">
        <table id="test-table" class="table table-hover">
            <thead>
                <tr>
                    <th>No</th>
                    <th>First Name</th>
                    <th>Last Name</th>
                    <th>Phone</th>
                    <th>Address</th>
                    <th>City</th>
                    <th>Country</th>
                </tr>
            </thead>
            <tbody>
            </tbody>

            <tfoot>
                <tr>
                    <th>No</th>
                    <th>First Name</th>
                    <th>Last Name</th>
                    <th>Phone</th>
                    <th>Address</th>
                    <th>City</th>
                    <th>Country</th>
                </tr>
            </tfoot>
        </table>

JS

<script type="text/javascript">

var table;

$(document).ready(function() {

//datatables
table = $('#test-table').DataTable({ 

    "processing": true, //Feature control the processing indicator.
    "serverSide": true, //Feature control DataTables' server-side processing mode.
    "order": [], //Initial no order.
    "ajax": {
        "url": "http://oss-dev.forexworld.us/oss/user/ajax_receiving",
        "type": "POST",
        data: {
            'csrf_test_name' : '802daa2efaf69edb83b571d7bf7510aa' 
            },
       dataSrc: function ( json ) {
           if(json.csrf_test_name !== undefined) $('meta[name=csrf_test_name]').attr("content", json.csrf_token);
           return json.data;
       }
    },
    "columnDefs": [
    { 
        "targets": [ 0 ], //first column / numbering column
        "orderable": false, //set not orderable
    },
    ],

});

});

</script>

I can make my datatable work by setting the $config['csrf_regenerate'] = TRUE; to FALSE but I don't want that. My guess is that the dataSrc part is not working but I'm not sure.

You can see it in action here .

Any help is highly appreciated. Thanks!

Refresh CSRF Token on AjaxPOST Datatables: CodeIgniter

The above statement is self explanatory & has clear meaning that:

We have to Refresh CSRF Token when we are making AjaxPOST

So, How we could Refresh CSRF Token in CodeIgniter & What is Mean Refresh CSRF in CodeIgniter.


If your AJAX uses a AjaxGET instead of a AjaxPOST, you will not have the issue as CSRF only works on POST data.


Alternatively, you need to get your new CSRF token and Hash and return it in your AjaxPOST call, then update the token and hash settings in your html or your javascript code.


Alternatively, you can make that particular url free from CSRF using the config element $config['csrf_exclude_uris'] = array();


Alternatively, you can set long time period to expire CSRF token using the config element $config['csrf_expire'] = 300; . Here 5 minutes is define to expire CSRF token, Before expire time (which start with first POST timestamp) you can use old CSRF token which was generated by CodeIgniter when you trigger first POST. If no POST trigger after first POST within 5 minutes, CodeIgniter will expire the CSRF token and after that you need to get New CSRF token. Sounds Complex.. No problem

Suppose you reload the page & CodeIgniter will set new CSRF hash, you can use that to make a AjaxPOST request. But if don't make any request or reload the page, after that within 5 minutes, The old CSRF will expire. Now if you call AjaxPOST request again, you get 500/403 error in response. That means CodeIgniter expire old CSRF hash because you made request after 5 minutes with old CSRF hash, that's actually not exist now.... Clear :)

So, what we can do to get new CSRF token when making AjaxPOST..?

  • Well, for the GET request No CSRF token required.
  • But for the POST Yes! you have to send CSRF with AjaxPOST if you using CodeIgniter CSRF Security $config['csrf_protection'] = TRUE;

The logic behind is, get new CSRF hash before you making any AjaxPOST by using GET. Set it somewhere in DOM or attach in AjaxSetup.

I am new in CodeIgniter .. :( Do you have any example..?

(Bulma!! Why you hit my Car again...) Yes Sure!!! :)

Here is View Html Code:

<!DOCTYPE html>
<html lang="en">
<head>

    <meta charset="utf-8">
    <meta name="<?=$this->security->get_csrf_token_name()?>" content="<?=$this->security->get_csrf_hash()?>"/>

    <title>CodeIgniter CSRF Refresh Demo</title>

    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

    <script type="text/javascript">

    // Set CodeIgniter base_url in JavaScript var to use within
    var _BASE_URL_ = "<?=base_url()?>";
    var _CSRF_NAME_ = "<?=$this->security->get_csrf_token_name()?>";

    (function($) {
        /**
         * New jQuery function to set/refresh CSRF token in Body & to attach AjaxPOST
         * @param  {[type]} $ [description]
         * @return {[type]}   [description]
         */
         $.fn.CsrfAjaxSet = function(CsrfObject) {
            // getting meta object from body head section by csrf name
            var CsrfMetaObj = $('meta[name="' + _CSRF_NAME_ + '"]'),
            CsrfSecret = {};
            // if CsrfObject not set/pass in function
            if (typeof CsrfObject == 'undefined') {
                // assign meta object in CsrfObject
                CsrfObject = CsrfMetaObj;
                // get meta tag name & value
                CsrfSecret[CsrfObject.attr('name')] = CsrfObject.attr('content');
            }
            // CsrfObject pass in function
            else {
                // get Csrf Token Name from JSON
                var CsrfName = Object.keys(CsrfObject);
                // set csrf token name & hash value in object
                CsrfSecret[CsrfName[0]] = CsrfObject[CsrfName[0]];
            }
            // attach CSRF object for each AjaxPOST automatically
            $.ajaxSetup({
                data: CsrfSecret
            });
        };
        /**
         * New jQuery function to get/refresh CSRF token from CodeIgniter
         * @param  {[type]} $ [description]
         * @return {[type]}   [description]
         */
         $.fn.CsrfAjaxGet = function() {
            return $.get(_BASE_URL_ + 'Csrfdata', function(CsrfJSON) {
                $(document).CsrfAjaxSet(CsrfJSON);
            }, 'JSON');            
        };
    })(jQuery);

    // On DOM ready attach CSRF within AjaxPOST
    $(document).CsrfAjaxSet();

</script>
</head>
<body>
<button class="button">Click Me to Trigger AjaxPOST</button>
    <script type="text/javascript">
        // on DOM ready
        $(document).ready(function(){
            // my button 
            $btn = $('.button');
            // on button click
            $btn.on('click', function(e){
                // stop default event
                e.preventDefault();
                // trigger refresh csrf token
                var CSRF = $(document).CsrfAjaxGet();
                // use callback to put your AjaxPOST & Done!
                CSRF.success(function(){
                    $.ajax({
                        url: _BASE_URL_ + 'Controller/Method',
                        method: 'POST',
                        success: function(data){
                            alert('Success');
                        },
                        error: function(xhr, status, error){
                            alert(error);
                        }
                    });
                });
            });
        });
    </script>
</body>
</html>

Here is Controller:

<?php 

(defined('BASEPATH') or exit('No direct script access allowed'));

/**
 * Class Csrfdata to return fresh CSRF hash value
 */
class Csrfdata extends CI_Controller
{

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Return CodeIgniter CSRF name & Hash on request
     * @return [type] [description]
     */
    public function index()
    {
        if ($this->input->get()) {
            $csrf = array();
            $csrf_name = $this->security->get_csrf_token_name();
            $csrf_hash = $this->security->get_csrf_hash();
            $csrf[$csrf_name] = $csrf_hash;
            echo json_encode($csrf);
        }

    }

}

/* End of file Csrfdata.php */
/* Location: ./application/controllers/Csrfdata.php */

Hope everybody will enjoy this answer, any suggestion, comments much appreciated... as always .. :D

Thank you Neeraj! Your solution worked for me.

I used the following code for AJAX DataTable in CodeIgniter. This is to trigger your CSRF Refresh method upon getting results from DataTable's AJAX call.

$('#table').DataTable({ 
    "ajax": {
        "url": "<?php echo site_url(); ?>list",
        "type": "POST",
        "dataSrc": function ( json ) {
            $(document).CsrfAjaxGet();
            return json.data;
        } 
    }
});

Please add this line in your page in script tag.

$(function($) {

    // this script needs to be loaded on every page where an ajax POST
    $.ajaxSetup({
        data: {
            '<?php echo $this->security->get_csrf_token_name(); ?>' : '<?php echo $this->security->get_csrf_hash(); ?>'
        }
    });


  // now write your ajax script 

});

Let me know if it not works

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM