【问题标题】:Codeigniter ajax CSRF problemCodeigniter ajax CSRF问题
【发布时间】:2011-11-12 23:39:21
【问题描述】:

我制作了一个简单的自动加载功能,当您在网站上向下滚动时加载内容。但是,当我在 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,一切都会很好......

【问题讨论】:

    标签: javascript ajax codeigniter token


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      正如其他人所说 - 您必须使用 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
          });
      });
      

      【讨论】:

      • 您好,georgiar,谢谢您的建议。我现在可以在页面加载时通过 AJAX 加载数据。我有一个按钮,它调用在页面加载时触发的相同函数。但是,单击时,我收到此错误消息“不允许您请求的操作。”
      【解决方案3】:

      如果需要,您可以在适当的地方同时回显令牌名称和哈希值。像这样。

       echo $this->security->get_csrf_token_name()
      

       echo $this->security->get_csrf_hash()
      

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

      【讨论】:

        【解决方案4】:

        回顾了我的情况,我认为最好的选择是使用 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'));

        【讨论】:

          【解决方案5】:

          基本上,您需要做的是从 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

          【讨论】:

            【解决方案6】:

            以前的建议效果很好,但我发现使用 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 版本)

            【讨论】:

              【解决方案7】:

              上述几个答案的唯一问题是 csrf 令牌仅对一个请求有效,因此如果您通过 ajax 发出 post 请求并且不刷新页面,您将没有当前的 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:token,将 crf_token 替换为您在 application/config/config.php 中找到的令牌的名称,上面写着 $config['csrf_token_name'] = 'csrf_token';

              【讨论】:

                猜你喜欢
                • 2011-11-13
                • 1970-01-01
                • 2011-11-22
                • 1970-01-01
                • 2011-09-28
                • 2021-08-16
                • 2011-07-19
                • 2012-02-03
                • 2017-07-01
                相关资源
                最近更新 更多