Codeigniter ajax CSRF 问题

8
我已经制作了一个简单的自动加载函数,当您在网站上向下滚动时会加载内容。然而,在启用Codeigniter中的CSRF保护时,似乎存在一些问题。 我没有使用表单,所以我不知道在进行滚动时如何从A发送令牌到B来执行我的POST请求。 我的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个回答

14

你可以尝试我曾经使用过的这段代码,它非常好用:

<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>

12

就像其他人说的一样 - 你必须将CSFR令牌名称和它的值与AJAX请求参数一起发布。这里有一个简单的解决方案,可以自动将其附加到每个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
    });
});

嗨,georgiar,感谢您的建议。我现在可以在页面加载时通过AJAX加载数据。我有一个按钮调用相同的函数,该函数在页面加载时触发。但是,当单击时,我收到此错误消息“您请求的操作不被允许。” - user752746

10

如果你愿意的话,可以在适当的位置输出令牌名称和哈希值。像这样:

 echo $this->security->get_csrf_token_name()

并且

 echo $this->security->get_csrf_hash()

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


2

经过审查我的情况,我认为最好的选择是使用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'));

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我不确定)


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


0
上述答案中唯一的问题是,csrf令牌只对一个请求有效。因此,如果您通过ajax进行了post请求,并且没有刷新页面,则下一个ajax post请求将无法获得当前的csrf令牌。这是我的解决方案: 在您的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 post请求的新csrf令牌

显然,您可以以其他方式输出此内容,但JSON通常与ajax调用一起使用。还要在每个post响应中包含此令牌,以便用于下一个post请求

然后,在您的下一个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:token 时,将其中的 crf_token 替换为在应用程序/config/config.php文件中找到的令牌名称,该文件中有一行声明 $config['csrf_token_name'] = 'csrf_token';


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,