How to Send AJAX request with CSRF token in CodeIgniter

1596880181 How to Send AJAX request with CSRF token in CodeIgniter

Cross-Site Request Forgery (CSRF) is a way to trick the server that a request sent to it is legitimate while it is an unauthorized attempt.

In CodeIgniter, CSRF protection is not enabled by default.

If it is been enabled then CodeIgniter generates a hash for each active user and this is used to verify the request.

Require to send the hash with the AJAX request otherwise, it gives error – “The action you have requested is not allowed.”.

In this tutorial, I show how you can enable CSRF protection and regenerate hash for the next AJAX request and pass hash in AJAX request in the CodeIgniter project.

 


1. Table structure

In this example, I am using users table and added some records –

CREATE TABLE `users` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(80) NOT NULL,
  `username` varchar(80) NOT NULL,
  `gender` varchar(10) NOT NULL,
  `email` varchar(80) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2. Configuration

Enable CSRF protection –

Open application/config/config.php file and update the values of $config['csrf_protection'], $config['csrf_token_name'], and $config['csrf_regenerate'] like this –

$config['csrf_protection'] = TRUE;  // Enable CSRF
$config['csrf_token_name'] = 'csrf_hash_name'; // Token name (You can update it)
$config['csrf_regenerate'] = TRUE; // Set TRUE to regenerate Hash
  • Set TRUE the $config['csrf_protection'], this will enable CSRF.
  • You can change the value of $config['csrf_token_name'] default it is set to 'csrf_test_name'. I changed it to 'csrf_has_name'. This name is used in AJAX request to pass the hash.
  • Set TRUE the $config['csrf_regenerate'] if you want to regenerate CSRF hash after each AJAX request otherwise set it FALSE.

Open application/config/database.php and define the Database connection.

$db['default'] = array(
 'dsn' => '',
 'hostname' => 'localhost',
 'username' => 'root', // Username
 'password' => '', // Password
 'database' => 'tutorial', // Database name
 'dbdriver' => 'mysqli',
 'dbprefix' => '',
 'pconnect' => FALSE,
 'db_debug' => (ENVIRONMENT !== 'production'),
 'cache_on' => FALSE,
 'cachedir' => '',
 'char_set' => 'utf8',
 'dbcollat' => 'utf8_general_ci',
 'swap_pre' => '',
 'encrypt' => FALSE,
 'compress' => FALSE,
 'stricton' => FALSE,
 'failover' => array(),
 'save_queries' => TRUE
);

Default controller

Open application/config/routes.php and edit default_controller value to User.

$route['default_controller'] = 'User';

Load Database

To access the MySQL database require loading database library.

Open application/config/autoload.php and add the database in libraries array().

$autoload['libraries'] = array("database");

3. Model

Create a Main_model.php file in the application/models/ directory.

Create getUserDetails() a method which takes a single parameter.

Check if $postData['username'] is set or not. If set then fetch a record from users table where username=$postData['username'].

Return the $response Array.

Completed Code

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Main_model extends CI_Model {

  function getUserDetails($postData){
 
    $response = array();
 
    if(isset($postData['username']) ){
 
      // Select record
      $this->db->select('*');
      $this->db->where('username', $postData['username']);
      $q = $this->db->get('users');
      $response = $q->result_array();
 
    }
 
    return $response;
  }

}

4. Controller

Create User.php file in application/controllers/ directory.

Create 2 methods –

  • index – Load url helper and user_view view.
  • userDetails – This method is used to handle the AJAX request and return a response.

Read POST values and assign in $postData. Load Main_model Model and call getUserDetails() method where pass $postData.

Assign return value in $data and read the new CSRF hash by calling $this->security->get_csrf_hash() for next AJAX request and assign in $data['token'].

Return $data Array in JSON format.

Completed Code

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class User extends CI_Controller {

   public function index(){

     // load base_url
     $this->load->helper('url');

     // load view
     $this->load->view('user_view');
   }

   public function userDetails(){

     // POST data
     $postData = $this->input->post();

     // load model
     $this->load->model('Main_model');

     // get data
     $data = $this->Main_model->getUserDetails($postData);

     // Read new token and assing in $data['token']
     $data['token'] = $this->security->get_csrf_hash();

     echo json_encode($data);
   }

}

5. View

Create user_view.php file in application/views/ directory.

Create a text element and store token name which is specified in application/config.php file in name attribute using <?= $this->security->get_csrf_token_name(); ?>. Store CSRF hash in the element by calling <?= $this->security->get_csrf_hash(); ?>.

Added some user names in the <select > <option>. Used username in the <option > value.

To display selected user details created <span > elements.

Script – 

Define change event on #sel_user selector.

Read name attribute of .txt_csrfname selector and assign in csrfName variable. Assign .txt_csrfname selector value in csrfHash variable.

Assign selected option value in username variable.

Send AJAX POST request to '<?=base_url()?>index.php/User/userDetails' where pass {username:username,[csrfName]:csrfHash } as data.

Here, the hash will pass like – [csrfName]: csrfHash.

On successful callback read the token from response and update the .txt_csrfname selector value.

Loop on the response[0] and update <span > elements text.

Completed Code

<!doctype html>
<html>
<head>
   <title>How to Send AJAX request with CSRF token in CodeIgniter</title>
</head>
<body>

    <!-- CSRF token (Here, name is 'csrf_hash_name' which is specified in $config['csrf_token_name'] in cofig.php file ) --> 
    <input type="text" class="txt_csrfname" name="<?= $this->security->get_csrf_token_name(); ?>" value="<?= $this->security->get_csrf_hash(); ?>"><br>   

    Select Username : <select id='sel_user'>
      <option value="user1">user1</option>
      <option value="user2">user2</option>
      <option value="user3">user3</option>
      <option value="user4">user4</option>
    </select>

    <!-- User details -->
    <div >
       Username : <span id='suname'></span><br/>
       Name : <span id='sname'></span><br/>
       Email : <span id='semail'></span><br/>
    </div>

    <!-- Script -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script type="text/javascript">
    // baseURL variable
    var baseURL= "<?= base_url();?>";

    $(document).ready(function(){

       $('#sel_user').change(function(){
          // CSRF Hash
          var csrfName = $('.txt_csrfname').attr('name'); // Value specified in $config['csrf_token_name']
          var csrfHash = $('.txt_csrfname').val(); // CSRF hash
          
          // Username
          var username = $(this).val();

          // AJAX request
          $.ajax({
            url:'<?=base_url()?>index.php/User/userDetails',
            method: 'post',
            data: {username: username,[csrfName]: csrfHash },
            dataType: 'json',
            success: function(response){

              // Update CSRF hash
              $('.txt_csrfname').val(response.token);

              // Empty the elements
              $('#suname,#sname,#semail').text('');
 
              // Loop on response
              $(response[0]).each(function(key,value){

                 var uname = value.username;
                 var name = value.name;
                 var email = value.email;

                 $('#suname').text(uname);
                 $('#sname').text(name);
                 $('#semail').text(email);
              });

            }
         });
       });
    });
    </script>
</body>
</html>

6. Demo

Select a username from the dropdown.


7. Conclusion

If you don’t want to regenerate hash for every AJAX then set FALSE to $config['csrf_regenerate'] in config.php file. In this case, the new hash will be generated according to the CSRF expiry time set.

 

Leave a Reply

Your email address will not be published. Required fields are marked *