CI: Why do all my jqxhr requests fail when $config['csrf_protection'] = TRUE?

2020-02-15 php jquery ajax http codeigniter-3

In going through my CI 3.1.11 code to clean it up (hosted by XAMPP v2.3.4 localhost) it seems I have done something to prevent the server from accepting any jqxhr requests if I have CI's Cross-Site Request Forgery (csrf) protection running.

I am using other http requsts to get information from the server and external files such as lines in the <head> that load jquery, my css file, etc. These work fine no matter the state of the csrf setting.

When I have set: $config['csrf_protection'] = TRUE; My jqxhr requests always get the http 403 error (rejected request that implies a suspected hacking attempt). My basic question is to understand why this happens so I can correct it and run the csrf protection.

I don't know if this is related but I found that I only need to provide the controller and function to the jqxhr rather than the complete url. Most examples I've seen online provide embedded Php code to insert the base_url in front. Something in the system (CI?) seems to want to prepend the base_url to the controller/function no matter what. If I insert it myself, it creates an unusable url with the base_url appearing twice.

If I use . . . var url= "C_library/updateTitle"; $.post(url, { 'tID':tID, 'newTitle':newTitle }, function(data) { . .

. . . the request works if csrf is not running. If csrf protection is running, this would be a typical console error report:

XHRPOSThttp://localhost/MZlocal/C_library/updateTitle [HTTP/1.1 403 Forbidden 32ms]

Note that the url is correctly formed.

Also, probably not relevant but here's my .htaccess file - which works (eliminates "index.php" from the address bar).

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [L]

csrf protection is an important feature of CI that I'd really like to understand better. Any help or hints as to where I should look is appreciated.

Full csrf config.php:

$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_test_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array();

Answers

In your Form data you must pass security token when CSRF is TRUE

<input type="hidden" name="<?php echo $this->security->get_csrf_token_name(); ?>" value="<?php echo $this->security->get_csrf_hash(); ?>" />

The whole idea of csrf-protection is having a token generated by your CI framework and beeing send to the server with every form submission or ajax request to get sure the request was made via your website. You have to send the token along with your request no matter if you submit a normal form or if you do an ajax request. If you do an ajax request there are 2 steps to make.

1) Make sure the token is provided in your sourcecode by either using the CodeIgniter form-helper (which is generating the token for you automatically) or by implementing a hidden input field (like @Boominathan Elango already posted)

<input type="hidden" name="<?php echo $this->security->get_csrf_token_name(); ?>" value="<?php echo $this->security->get_csrf_hash(); ?>" />

You could provide the token value in many different ways but this one is fine in your case I guess.

2) Then you have to send the token along with your ajax request:

In your Javascript code:

// first get the token value from your hidden input field:
var csrfTokenValue = $('input[name=csrf_test_name]').val();

// You Ajax url - check if this here is the right one for you
var ajaxUrl = 'http://localhost/MZlocal/C_library/updateTitle';

$.ajax({
  type: "POST",
  url: ajaxUrl,
  data: {
    'csrf_test_name' : csrfTokenValue,
    'tID': tID,
    'newTitle':newTitle
  },
  success: success,
  dataType: dataType
});

Assuming you have the config still like posted > csrf_test_name as token name. Maybe you have to alter your javascript a little bit (according to the data you want to send with your ajax request) but but in general it should work like that. Catching errors and so on..

Related