Codeigniter ajax CSRF问题

时间:2011-09-08 12:56:58

标签: javascript ajax codeigniter token

我制作了一个简单的自动加载功能,可以在网站上向下滚动时加载内容。但是,当我在Codeigniter中启用CSRF保护时似乎存在一些问题。

我没有使用表格,因此我不知道当你滚动时我正在做我的帖子请求时如何将令牌从A发送到B.

我的JavaScript

if (location.href == baseurl) {
    $(window).scroll(function(){
        if ($(window).scrollTop() > $('body').height() / 2) {
            if(doScroll == 1) {
                $.post(baseurl + 'ajax/images',{'id' : ID}, function(data) {
                    $("#wrapper_content").append(data);
                    if(data == 'Det finnes ikke flere bilder i databasen, WTF!? Send inn forslag ASAP!') {
                        doScroll = 0;
                    }
                    ID++;
                });
            }
        }
    });
}

由于Codeigniter期望在所有POST请求中都有TOKEN,因此当CSRF启用时,我无法使其工作。有什么建议吗?

CSRF启用时出错

  

无法加载资源:服务器响应状态为500(内部服务器错误)

如果我关闭CSRF,一切都很有效......

7 个答案:

答案 0 :(得分:13)

您可能想尝试我使用过的代码。效果很好:

<script type="text/javascript">
$(function(){
   $('.answerlist').each(function(e){

  $(this).click(function(){

    var valrad = $("input[@name=answer]:checked").val();


    var post_data = {
        'ansid': valrad,
        '<?php echo $this->security->get_csrf_token_name(); ?>' : '<?php echo $this->security->get_csrf_hash(); ?>'
    };

        $.ajax({
                type: "POST",
                url: "<?php echo base_url(); ?>online/checkanswer",
                data: post_data,
                success: function(msg){
                  /// do something 
                }
            });

  });

   });


});


</script>

答案 1 :(得分:13)

正如其他人所说 - 你必须使用AJAX请求参数发布CSFR令牌名称及其值。这是一个简单的解决方案,可以自动将其附加到每个AJAX请求。

以下是我在主视图中的内容,因此在加载其他javascript文件之前,此代码位于每个页面上:

   <script>
     var csfrData = {};
     csfrData['<?php echo $this->security->get_csrf_token_name(); ?>']
                       = '<?php echo $this->security->get_csrf_hash(); ?>';
   </script>
   <!-- ... include other javascript files -->
  </body>
</html>

这是我在每个页面上都包含的javascript文件的一部分:

$(function() {
    // Attach csfr data token
    $.ajaxSetup({
       data: csfrData
    });
});

答案 2 :(得分:10)

如果需要,您可以在适当的位置回显令牌名称和哈希值。这样的事情。

 echo $this->security->get_csrf_token_name()

 echo $this->security->get_csrf_hash()

或者,您可以像往常一样使用form_open()并使用从javascript为您生成的隐藏输入。禁用CSRF功能是错误的方法。

答案 3 :(得分:0)

基本上你需要做的是从cookie中获取预期的csrf值(默认名称为“ci_csrf_token”),然后将其与其他数据一起发布。

您需要修改此行:

$.post(baseurl + 'ajax/images',{'id' : ID}, function(data) {

为:

$.post(baseurl + 'ajax/images',{'id' : ID,'ci_csrf_token' : $.cookie('ci_csrf_token')}, function(data) {

可能需要安装cookie插件(我不太确定;我使用的是mootools)。以下是更多信息:http://aymsystems.com/ajax-csrf-protection-codeigniter-20

答案 4 :(得分:0)

以前的建议很有效,但我发现使用ajax设置自动将此标记应用于每个帖子,而不是使用可以在每个数据帖子中应用的变量,

$(document).ajaxSend(function(elm, xhr, s){
        if(s.data){
            s.data += '&';
        }
        s.data += '<?php echo $this->security->get_csrf_token_name(); ?>=<?php echo $this->security->get_csrf_hash(); ?>';
    }); 

(适用于jquery-1.9.1。我不确定其他jquery版本)

答案 5 :(得分:0)

上述一些答案的唯一问题是csrf令牌仅对一个请求有效,因此如果您通过ajax发布帖子请求并且不刷新页面,则您将没有当前的csrf令牌下一个ajax发布请求。这是我的解决方案:

在CodeIgniter控制器中:

$data = array('data'=> 'data to send back to browser');
$csrf =  $this->security->get_csrf_hash();
$this->output
    ->set_content_type('application/json')
    ->set_output(json_encode(array('data' => $data, 'csrf' => $csrf)));

$ data =要返回浏览器的数据

$ csrf =浏览器用于下一个ajax发布请求的新csrf令牌

显然你可以用其他方式输出它,但JSON主要用于ajax调用。还要在每个帖子响应中包含此标记,以用于下一个帖子请求

然后在你的下一个ajax请求(javascript)中:

var token = data.csrf;

$.ajax({
    url: '/next/ajax/request/url',
    type: 'POST',
    data: { new_data: 'new data to send via post', csrf_token:token },
    cache: false,
    success: function(data, textStatus, jqXHR) {
        // Get new csrf token for next ajax post
        var new_csrf_token = data.csrf     
       //Do something with data returned from post request
    },
    error: function(jqXHR, textStatus, errorThrown) {
      // Handle errors here
      console.log('ERRORS: ' + textStatus + ' - ' + errorThrown );
    }
});

另请注意,我已经csrf_token:tokencrf_token替换为您在app / config / config.php中找到的令牌名称$config['csrf_token_name'] = 'csrf_token';

答案 6 :(得分:0)

在审核了我的情况之后,我认为最好的选择是使用CSRF,但在每次尝试时重置令牌。否则,之前表达的关于重新使用cookie令牌的想法将允许攻击者使用相同的令牌重新提交数据数百次,这会使得该点的对象失效。

因此我创建了以下功能:

public function resetCSRF(){    

    $this->security = null;

    $_COOKIE[$this->config->item('csrf_cookie_name')] = null;

    load_class('Security', 'core');

    $this->security->csrf_set_cookie();

return $this->security->get_csrf_hash();
}

例如,如果基于ajax的登录表单失败 - 在PHP中调用此函数,然后在接收失败的javascript端(此解决方案使用Jquery和来自w3schools的getCookie函数)将调用:

$('input[name="csrf_test_name"]').val(getCookie('csrf_cookie_name'));