JSON请求中的CodeIgniter CSRF令牌

时间:2013-09-14 06:10:42

标签: json codeigniter csrf

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

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

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

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

4 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:1)

要修复该问题,我必须更改位于“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标头值。但令人惊讶的是,这并不是直截了当的......

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

干杯

答案 2 :(得分: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;
    }

}

答案 3 :(得分:0)

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

这是我的解决方案,为我工作,我检查应用程序/ 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;
    }

}
相关问题