How to include CSRF from Codeigniter into ajax dat

2019-01-19 04:10发布

问题:

I am trying to pass some data into my Controller, but I'm getting a 500 error. After some research, I discovered that it's caused by the CSRF token not being sent.

It seems like I need to include this along with the data: <?php echo $this->security->get_csrf_token_name(); ?>:"<?php echo $this->security->get_csrf_hash(); ?>"

My JS is pretty weak, so I'm a little confused on how to alter this to include the above.

<script type="text/javascript"> 
$(document).ready(function() {
    $("#order").sortable({
        update : function (event, ui) {
            order = $('#order').sortable('serialize');
            $.ajax({
                url: "<?=base_url().'admin/category/update_order'?>",
                type: "POST",
                data: order,
                success: function(response){
                    console.log(response);
                }
            });
        }
    });
}
);
</script>

回答1:

The token needs to be passed in the data argument of $.ajax.

This should work but see my notes below.

order['<?php echo $this->security->get_csrf_token_name(); ?>'] = '<?php echo $this->security->get_csrf_hash(); ?>';

However, there are a few bad practices going on here. Mainly you should not use PHP in your javascript because this prevents you from being able to access the javascript as a separate file (this is good because browsers will cache it to make your page load faster and consume less bandwidth).

It's better to store the token in your order <form> html like this..

<input type="hidden" name="<?php echo $this->security->get_csrf_token_name(); ?>" value="<?php echo $this->security->get_csrf_hash(); ?>" />

Then it will get serialized with the rest of your form data.

You can also store the URL in the form's action attribute. This will help your script gracefully degrade and also keeps the URL in one place instead of 2.

<form id="order" method="post" action="<?=base_url()?>admin/category/update_order">

In the $.ajax call, use something like this url: $('#order').attr('action'), assuming #order is the actual form id.



回答2:

CI stores csrf in cookie and you can fetch it from there:

var csrf = $.cookie('csrf_cookie_name');

The downside of this method is that jQuery doesn't natively provide cookie access. So you will need a jquery plugin.



回答3:

You're correct, just add the CSRF token to your post data. You can use jQuery's $.extend function to merge your order object you already created with the CSRF token data, like this:

$.extend(alerts, {
'<?php echo $this->security->get_csrf_token_name(); ?>' :
'<?php echo $this->security->get_csrf_hash(); ?>' });


回答4:

Here's a different approach. Simple function in Auth.php that returns the csrf token name and hash in JSON format. Then, in our javascript, make two ajax calls, the first to grab the csrf creds and insert them into hidden form fields, the second to handle our actual form submit.

// place function in Auth.php controller

public function get_csrf()
{
    $csrf['csrf_name'] = $this->security->get_csrf_token_name();
    $csrf['csrf_token'] = $this->security->get_csrf_hash();

    echo json_encode($csrf);
}

// myFunction()

<script type="text/javascript">
function myFunction() {

    // first ajax call to grab the csrf name and token
    // from our get_csrf() function in Auth.php
    $.ajax({
        type: "GET",
        dataType: 'json',
        url: "https://<your_domain>.com/auth/get_csrf", //replace with your domain
        success: function (data) {

            // assign csrf name and token to variables
            csrf_name = data.csrf_name;
            csrf_token = data.csrf_token;

            // assign field1 and field2 field values to variables
            var form_field1 = document.getElementById('field1').value;
            var form_field2 = document.getElementById('field2').value;

            // insert csrf creds into form
            $('#csrftoken').attr('name', csrf_name);
            $('#csrftoken').val(csrf_token);

            // second ajax call -- this is our form handler
            $.ajax({
                type: "POST",
                url: "https://<your_domain>.com/<your_controller>/<your_function>", //replace with your domain/controller/function
                data: {
                    // ES6 computed property keys are great...
                    [csrf_name] : csrf_token,
                    "field1" : form_field1,
                    "field2" : form_field2
                      },
                success: function(data) {

                    // handle the return from your form submit
                    if (data == "ok") {
                        console.log("Success!");
                        $("#infoMessage").html("<p>Success</p>").fadeIn(300);

                    } else {

                        console.log("Oops!");
                        $("#infoMessage").html("<p>Oops!</p>");

                    }

                }
            });

        }
    });

}
</script>

// html

<div id="infoMessage"></div>

<form method="post" accept-charset="utf-8">

    <input type="hidden" id="csrftoken" name="" value="" />

    <label for="field1">Field 1:</label> <br />
    <input type="text" name="field1" id="field1"  />

    <label for="field2">Field 2:</label> <br />
    <input type="text" name="field2" id="field2"  />

    <input type="button" name="submit" value="SUBMIT" onclick="myFunction()" />

</form>


回答5:

I extend form_helper as MY_form_helper.php to serve csrf tokens to my own form generation code - you could use something similar:

    function secure_form() {
        $CI =& get_instance();
        return '<input type="hidden" name='.$CI->security->get_csrf_token_name().' value='.$CI->security->get_csrf_hash().'>';
    }


回答6:

another solution is to use .serialize():

$.post("user/save", $('#frmUser').serialize(),function(html){
        $("#results").html(html);
});

that will find the hidden field that stores the CSRF data.