【问题标题】:Codeigniter CSRF valid for only one time ajax requestCodeigniter CSRF 仅对一次 ajax 请求有效
【发布时间】:2016-07-21 11:04:35
【问题描述】:

我想在 jQuery 的更改事件上将图像上传到服务器上,但使用 codeigniter csrf 我只能上传一次图像。如何使用 ajax 为多个请求上传图像。设置时请记住

config['csrf_protection'] = FALSE;

然后我可以发送多个请求 jQuery onchange 事件,但是当 csrf_protection 为假时,我认为 csrf 没有优势。所以问题是如何在启用 csrf_protection 时使用 ajax 发送多个请求。我的 jquery 代码如下

$("#avatar").change(function(){
    var link = $("#avatar").val();     
    $.ajax({
        url : "<?php echo base_url('main/test'); ?>",
        type: 'post',
        data: {'<?php echo $this->security->get_csrf_token_name(); ?>':'<?php echo $this->security->get_csrf_hash(); ?>',"id":"hello","link":link},            
        success : function(data)
        {   
            alert(data);
        }  
    });
});

我的控制器:

public function test()
{
    $config['upload_path'] = './uploads/';
    $config['allowed_types'] = 'gif|jpg|png';
    $config['max_size'] = 500;
    $config['max_width'] = 260;
    $config['max_height'] = 260;
    $this->load->library('upload', $config);
    if (!$this->upload->do_upload('link')) {
        echo "error";
    } else {
        $data = array('upload_data' => $this->upload->data());
        $image_name = $data['upload_data']['file_name'];
        echo $image_name;
    }
}

【问题讨论】:

    标签: php jquery ajax codeigniter csrf


    【解决方案1】:

    在我看来,您应该尝试在每个请求中重新创建您的 csrf 令牌

    试试这个代码示例...

    对于js函数

    var csrfName = '<?php echo $this->security->get_csrf_token_name(); ?>',
        csrfHash = '<?php echo $this->security->get_csrf_hash(); ?>';
    ("#avatar").change(function(){
        var link = $("#avatar").val();
    
        var dataJson = { [csrfName]: csrfHash, id: "hello", link: link };
    
        $.ajax({
            url : "<?php echo base_url('main/test'); ?>",
            type: 'post',
            data: dataJson,            
            success : function(data)
            {   
                csrfName = data.csrfName;
                csrfHash = data.csrfHash;
                alert(data.message);
            }  
        });
    });
    

    对于控制器

    public function test() { 
        $config['upload_path'] = './uploads/'; 
        $config['allowed_types'] = 'gif|jpg|png'; 
        $config['max_size'] = 500; 
        $config['max_width'] = 260; 
        $config['max_height'] = 260; 
    
        $reponse = array(
                    'csrfName' => $this->security->get_csrf_token_name(),
                    'csrfHash' => $this->security->get_csrf_hash()
                    )
    
        $this->load->library('upload', $config); 
        if (!$this->upload->do_upload('link')) { 
            $reponse['message'] = "error"; 
        } 
        else { 
            $data = array('upload_data' => $this->upload->data()); 
            $image_name = $data['upload_data']['file_name']; 
            $reponse['message'] = $image_name; 
        } 
    
        echo json_encode($reponse);
    }
    

    告诉我,祝你好运

    注意:当有人要求您为问题发布更多数据时,不要将其作为评论或答案发布,最好编辑问题本身并添加内容

    【讨论】:

    • 嗨,Edu,关于如何在每个请求中重新创建您的 csrf 令牌有什么建议吗?提前感谢您的见解。
    【解决方案2】:

    你可以在config.php中设置这个

    $config['csrf_regenerate'] = FALSE;
    

    所以 csrf 保护在所有会话时间内都有效,它将解决您的问题。 如果你设置 $config['csrf_regenerate'] = true; 然后 CI 为每个请求生成新的 csrf 令牌,因此您的旧 csrf 令牌与新生成的 csrf 令牌不匹配

    【讨论】:

    • 我查了很多,不推荐使用这种方式,会造成安全损失,还有其他方式。
    • 我同意 Marcelo CSRF 保护对于公共网络应用程序是必要的。
    • 我发现总是重新生成 csrf 令牌并不是最好的方法,它给多个选项卡(示例)带来的问题可能会破坏你的应用程序,所以还有其他方法。很遗憾 CI 没有任何解决方案,这真的让我很伤心,我要离开代码点火器(还有其他原因)。
    • 我记得我遇到了同样的麻烦,但我找到了解决方法,我认为你应该在 CI 的论坛上发布这个问题。对我来说,CI 是中小型网站的一个很好的框架,我认为 CI 4 很快就会改变,它是一个由非营利团队维护的很好的开源项目。
    【解决方案3】:

    $config['csrf_regenerate'] = TRUE;

    将 auto generate 设置为 true 会更安全。 在第一个请求中 csrf 过期的类似情况下。我实现了什么

    $(document).ajaxComplete(function (event, xhr, settings) {
     let response = xhr.responseText,
     let obj = JSON.parse(response),
     let csrfData = obj.csrf;
     document.querySelector('input[name="' + csrfData.name + '"]').value = csrfData.hash; 
    }); //Also here you can update any other non input element    
    

    在每个 ajax 响应中,我们都传递 csrf 数据,其中最新的 csrf 数据将替换为当前数据

    请求响应示例

    { 
    csrf : {
      name : 'csrf_name',
      hash : 'qw76sd7s6f78sdfs8dfs9df8cx9'
     }
    }
    

    我在每个 ajax 请求中更新 csrf 令牌。如果您正在使用多选项卡环境,也不要选择此方法

    【讨论】:

      【解决方案4】:

      每次您提出请求时,CI 都会更新csrf_token。这就是 CSRF 只工作一次的原因。所以每次我们提出请求时,我们也需要更新 csrf_token。我通过这样做解决了这个问题。

      控制器使用此代码获取更新后的 csrf

      public function update_csrf()
      {
        $data['csrf_hash'] = $this->security->get_csrf_hash();
        echo json_encode($data);
      }
      

      AJAX替换你的旧值 csrf name="csrf_token_name"

      var jqXHR = $.ajax({
                  url: $(this).attr('action'),
                  type: 'POST',
                  data: $(this).serialize(),
                  dataType: 'json',
              })
              jqXHR.done(function(response) {
                  $('input[name=csrf_token_name]').val(response.csrf_hash); //update the csrf to the form 
              })
              jqXHR.fail(function(jqXHR, textStatus, errorThrown) {
                  console.log(jqXHR);
                  console.log(textStatus);
                  console.log(errorThrown);
              });
      

      重要!:使用dataType: 'json'

      所以现在每次您成功请求时,csrf_token 也会更新,您现在不会出现 403(禁止)错误

      【讨论】:

        【解决方案5】:

        您需要做的就是在您的 AJAX 响应中重新加载 CSRF 令牌。就是这么简单!

        【讨论】:

        • 您所需要的只是您的 ajax 控制器的响应是 json 格式,并且在您的 json 中您可以传递新的 csrf 令牌,您的前端解析 json 并替换表单隐藏输入中的 scrf 值。够清楚吗?
        【解决方案6】:

        在每个页面加载的 js 文件中添加这个(我把它放在 jquery.js 的末尾)

            $.ajaxSetup({
                beforeSend:function(jqXHR, Obj){
                    var value = "; " + document.cookie;
                    var parts = value.split("; csrf_cookie_name=");
                    if(parts.length == 2)   
                    Obj.data += '&csrf_token='+parts.pop().split(";").shift();
                }
            });
        

        (请注意,在每个 ajax 请求中,您不能有空数据要发送)

        "csrf_cookie_name" 在顶部定义在 config.php

        $config['csrf_cookie_name'] = 'csrf_cookie_name';
        

        【讨论】:

          【解决方案7】:

          请尝试像我的代码一样。它在我的应用程序中工作

          您的视图文件

          $token_name = $this->security->get_csrf_token_name();
          $token_hash = $this->security->get_csrf_hash();
          
          <input type="text" id="search-text" name="parent_name" placeholder="Search" value=""  >
          <input type="hidden" id="csrf" name="<?php echo $token_name; ?>" value="<?php echo $token_hash; ?>" />
          

          设置 jquery post 方法后,如下所示

          // Get Keyup 
          jQuery( "#search-text").keyup(function() {
              // Get Data 
              var val       = jQuery("#search-text").val();
              var hashValue = jQuery('#csrf').val();
          
              // Get jquery post for ajax task
              jQuery.post( 
                  '<?php echo $base_controler; ?>',
                  {val:val,'<?php echo $this->security->get_csrf_token_name(); ?>':hashValue}, 
                  function(data)
                  { 
                      // Get return data to decode
                      var obj = jQuery.parseJSON(data);
                      // Get csrf new hash value
                      var new_hash = obj.csrfHash;
                      // Set csrf new hash value update
                      jQuery('#csrf').val(new_hash);
          
                  }
              );        
          
          });
          

          请像下面这样设置你的控制器

          $reponse = array(
                  'csrfName' => $this->security->get_csrf_token_name(),
                  'csrfHash' => $this->security->get_csrf_hash()
                  );
          echo json_encode($reponse);
          

          在所有代码中,每个请求都会重新创建您的 csrf 令牌。

          【讨论】:

            【解决方案8】:

            也许你可以尝试使用 jquery cookie

            首先你需要添加这个

            <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>
            

            然后将您的代码更改为此

            $("#avatar").change(function(){
            var link = $("#avatar").val();     
            $.ajax({
                url : "<?php echo base_url('main/test'); ?>",
                type: 'post',
                data: {csrf_test_name: $.cookie('csrf_cookie_name'),"id":"hello","link":link},
                dataType : "JSON",
                success : function(data)
                {   
                    alert(data);
                }  
            });
            

            最后你可以尝试将你的 csrf_protection 设置为 true

            [csrf_protection] = TRUE;
            

            【讨论】:

              【解决方案9】:

              编辑配置:

              $config['csrf_exclude_uris'] = ['controller/method'];
              

              数组可以包含您希望禁用 csrf 保护的所有列入白名单的控制器/方法。

              数组还可以处理正则表达式如:

              $config['csrf_exclude_uris'] = array(
                                                      'api/record/[0-9]+',
                                                      'api/title/[a-z]+'
                                              );
              

              欲了解更多信息,请访问Codeigniter Documentation - Security Class

              【讨论】:

              • 请注意;虽然这个答案对于 API 来说可能很好,但对于 ajax 或其他后请求来说,这不是一个好习惯。禁用 CSRF 可能会导致安全问题。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-09-01
              • 2015-10-26
              • 1970-01-01
              • 2015-08-28
              • 1970-01-01
              相关资源
              最近更新 更多