首页 文章

JSON请求中的CodeIgniter CSRF令牌

提问于
浏览
1

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

我使用Content-Type“application / json将HTTP POST请求发送到我的PHP后端 . 有效负载是JSON数据 . 除了数据之外,我还传递生成并存储在CSRF cookie中的CSRF令牌 . 使用标准POST FORM请求,它工作得很好,但是当作为JSON发送时它失败了 .

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

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

4 回答

  • 2

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

    $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;
    }
    
  • 0

    要解决这个问题,我不得不更改位于“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标头值 . 但令人惊讶的是,这并不是直截了当的......

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

    干杯

  • 0

    如果需要重写,最好扩展安全库,而不是直接编辑核心文件 .

    在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;
        }
    
    }
    
  • 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;
        }
    
    }
    

相关问题