【问题标题】:CodeIgniter CSRF token in JSON requestJSON 请求中的 CodeIgniter CSRF 令牌
【发布时间】:2013-09-18 21:13:08
【问题描述】:

我遇到了 CodeIgniter / CSRF / JSON 的问题。

我使用 Content-Type "application/json" 向我的 PHP 后端发送 http POST 请求。有效负载是 JSON 数据。与数据一起,我传递生成并存储在 CSRF cookie 中的 CSRF 令牌。使用一个标准的 POST FORM 请求,它工作得很好,但是当作为 JSON 发送时它失败了。

由于 JSON 内容类型,$_POST 数组为空,CodeIgniter 无法验证 cookie 并引发错误。

如何让 CodeIgniter 检查 JSON 有效负载并验证我的 CSRF 令牌?

【问题讨论】:

    标签: json codeigniter csrf


    【解决方案1】:

    正如 Brian 所写,您必须将自定义类放入 /application/core/ ex。 My_Security.php

    这是我的解决方案,为我工作,我检查 application/json content_type 并请求 cookie。

    defined('BASEPATH') OR exit('No direct script access allowed');
    
    class MY_Security extends  CI_Security {
    
    
        public function csrf_verify()
        {
    
            // If it's not a POST request we will set the CSRF cookie
            if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
            {
                return $this->csrf_set_cookie();
            }
    
                    /**
                     *  mine implementation for application/json
                     */
                    $reqHeaders = getallheaders();
                    $content_type = $reqHeaders["Content-Type"];
    
                    #it's a json request?
                    if(preg_match("/(application\/json)/i",$content_type))
                    {
                        #the check the cookie from request
                        $reqCookies = explode("; ",$reqHeaders["Cookie"]);
                        foreach($reqCookies as $c)
                        {
                            if(preg_match("/(".$this->_csrf_cookie_name."\=)/", $c))
                            {
                              $c = explode("=",$c);
    
                              if($_COOKIE[$this->_csrf_cookie_name] == $c[1])
                              {
                                 return $this; 
                              }
                            }
                        }
    
                    }
                    //< end
    
            // Check if URI has been whitelisted from CSRF checks
            if ($exclude_uris = config_item('csrf_exclude_uris'))
            {
                $uri = load_class('URI', 'core');
                foreach ($exclude_uris as $excluded)
                {
                    if (preg_match('#^'.$excluded.'$#i'.(UTF8_ENABLED ? 'u' : ''), $uri->uri_string()))
                    {
                        return $this;
                    }
                }
            }
    
            // Do the tokens exist in both the _POST and _COOKIE arrays?
            if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])
                OR $_POST[$this->_csrf_token_name] !== $_COOKIE[$this->_csrf_cookie_name]) // Do the tokens match?
            {
                $this->csrf_show_error();
            }
    
            // We kill this since we're done and we don't want to polute the _POST array
            unset($_POST[$this->_csrf_token_name]);
    
            // Regenerate on every submission?
            if (config_item('csrf_regenerate'))
            {
                // Nothing should last forever
                unset($_COOKIE[$this->_csrf_cookie_name]);
                $this->_csrf_hash = NULL;
            }
    
            $this->_csrf_set_hash();
            $this->csrf_set_cookie();
    
            log_message('info', 'CSRF token verified');
            return $this;
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      如果这需要被覆盖,最好扩展安全库而不是直接编辑核心文件。

      在 application/core/ 中创建文件 My_Security.php 并添加以下内容(来自上面的解决方案):

      <?php
      class My_Security extends CI_Security {
          /**
           * Verify Cross Site Request Forgery Protection
           *
           * @return  object
           */
          public function csrf_verify()
          {
              // If it's not a POST request we will set the CSRF cookie
              if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
              {
                  return $this->csrf_set_cookie();
              }
      
              // Do the tokens exist in both the _POST and _COOKIE arrays?
              if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])) {
                  // No token found in $_POST - checking JSON data
                  $input_data = json_decode(trim(file_get_contents('php://input')), true);
                  if ((!$input_data || !isset($input_data[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])))
                      $this->csrf_show_error(); // Nothing found
                  else {
                      // Do the tokens match?
                      if ($input_data[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
                          $this->csrf_show_error();
                  }
              }
              else {
                  // Do the tokens match?
                  if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
                      $this->csrf_show_error();
              }        // We kill this since we're done and we don't want to
      
              // polute the _POST array
              unset($_POST[$this->_csrf_token_name]);
      
              // Nothing should last forever
              unset($_COOKIE[$this->_csrf_cookie_name]);
              $this->_csrf_set_hash();
              $this->csrf_set_cookie();
      
              log_message('debug', 'CSRF token verified');
      
              return $this;
          }
      
      }
      

      【讨论】:

        【解决方案3】:

        或者,您可以通过在第 351 行下方的 application/config/config.php 中添加以下代码来跳过 CSRF 检查(基于 CI 2.1.4)。

         $config['csrf_expire'] = 7200; // This is line no. 351
        
        /* If the REQUEST_URI has method is POST and requesting the API url,
            then skip CSRF check, otherwise don't do. */
        if (isset($_SERVER["REQUEST_URI"]) &&
           (isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'POST') ))
        {
            if (stripos($_SERVER["REQUEST_URI"],'/api/') === false )  { // Verify if POST Request is not for API
                $config['csrf_protection'] = TRUE;
            }
            else {
                $config['csrf_protection'] = FALSE;
            }
        } else {
            $config['csrf_protection'] = TRUE;
        }
        

        【讨论】:

          【解决方案4】:

          为了解决这个问题,我不得不更改位于“system/core/”中的“Security.php”文件的代码。

          在函数“csrf_verify”中,替换该代码:

          // Do the tokens exist in both the _POST and _COOKIE arrays?
          if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]))
          {
          $this->csrf_show_error();
          }
          // Do the tokens match?
          if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
          {
          $this->csrf_show_error();
          }
          

          通过该代码:

          // Do the tokens exist in both the _POST and _COOKIE arrays?
          if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])) {
              // No token found in $_POST - checking JSON data
              $input_data = json_decode(trim(file_get_contents('php://input')), true); 
              if ((!$input_data || !isset($input_data[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])))
                  $this->csrf_show_error(); // Nothing found
              else {
                  // Do the tokens match?
                  if ($input_data[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
                      $this->csrf_show_error();
              }
          }
          else {
              // Do the tokens match?
              if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
                  $this->csrf_show_error();
          }
          

          该代码首先检查 $_POST 然后如果没有找到,它会检查 JSON 有效负载。

          执行此操作的理想方法是检查传入请求的 Content-Type 标头值。但令人惊讶的是,这并不是直截了当的……

          如果有人有更好的解决方案,请在此处发布。

          干杯

          【讨论】:

          猜你喜欢
          • 2016-01-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-21
          • 2018-11-22
          • 2014-08-10
          • 2011-09-28
          • 2016-07-14
          相关资源
          最近更新 更多