CodeIgniter CSRF token in JSON request
Alternatively, you can skip the CSRF checking by adding following code on application/config/config.php below Line No. 351 (based on 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;}
To fix that issue, I had to change the code of the "Security.php" file located in "system/core/".
In function "csrf_verify", replace that code:
// 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();}
By that code:
// 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();}
That code first checks $_POST then if nothing has been found, it checks the JSON payload.
The ideal way of doing this would be to check the incoming request Content-Type header value. But surprisingly, it's not straight forward to do ...
If someone has a better solution, please post it here.
Cheers
If this needs to be overridden, best to extend the Security library rather than editing the core file directly.
Create the file My_Security.php in application/core/ and add the following (from the solution above):
<?phpclass 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; }}