I have admin page with multiple settings, each setting have different form in different tab.
I am using ajax and to save data, and i didn't had any problems so far with csrf token when i had only one form on a page, or when i disable csrf token.
On each ajax request new token is generated in controller and sent back to ajax which is updating hidden field with name="csrf_token"
but with different id's.
After first form is submitted all is good, but when i try to submit other form csrf token doesn't work anymore i am getting message "The action you have requested is not allowed." with page 403 in console output even after i reload page and try to submit other form that didn't worked.
Is there way to have multiple forms with csrf protection on same page and how to handle that ?
Here is code examples Forms with ajax
<form id="upload-icon" method="POST" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" id="csrf_token_1" value="<?php echo $this->security->get_csrf_hash(); ?>">
<input type="file" id="favicon_image" name="favicon_image" accept=".ico">
<button type="button" id="upload-icon-btn">Upload</button>
</form>
<form id="update-settings" method="POST">
<input type="hidden" name="csrf_token" id="csrf_token_2" value="<?php echo $this->security->get_csrf_hash(); ?>">
<input type="text" name="settings_one">
<input type="text" name="settings_two">
<input type="text" name="settings_three">
<button type="button" id="update-settings-btn">Update settings</button>
</form>
<script>
$(document).ready(function() {
var csrf_token = '';
// upload favicon form
$('#upload-favicon-form-btn').on('click', function(e) {
e.preventDefault();
var fd = new FormData();
var files = $('#favicon_image')[0].files[0];
fd.append('favicon_image', files);
var favicon = $('#favicon_image').val();
if (favicon == '') {
loadModal('Warning', 'Please select <strong>favicon.ico</strong> icon file.');
} else {
$.ajax({
type: 'POST',
url: '<?php echo base_url('admin/settings/upload_ico'); ?>',
data: fd,
contentType: false,
cache: false,
processData: false,
dataType: 'json',
success: function(response) {
csrf_token = response.csrf_token;
$('#csrf_token_1').val(csrf_token);
// messages output
},
error: function() {
// error message output
}
});
}
});
// update settings form
$('#update-settings-btn').on('click', function(e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '<?php echo base_url('admin/settings/update_settings'); ?>',
data: $('#update-settings').serialize(),
dataType: 'json',
success: function(response) {
csrf_token = response.csrf_token;
$('#csrf_token_2').val(csrf_token);
// messages output
},
error: function() {
// error message output
}
});
});
});
</script>
Settings controller
public function update_settings()
{
$csrf_token = $this->security->get_csrf_hash();
$this->form_validation->set_rules('settings_one', 'Setting one', 'trim|required|xss_clean');
$this->form_validation->set_rules('settings_two', 'Setting two', 'trim|required|xss_clean');
$this->form_validation->set_rules('settings_three', 'Setting three', 'trim|required|xss_clean');
if ($this->form_validation->run()) {
if ($this->Settings_model->UpdateSettings($this->input->post('settings_one'), $this->input->post('settings_two'), $this->input->post('settings_three'))) {
$data = array(
'success' => true,
'message' => 'Settings updated.',
'csrf_token' => $csrf_token
);
} else {
$data = array(
'error' => true,
'message' => 'Settings was not updated.',
'csrf_token' => $csrf_token
);
}
} else {
$data = array(
'error' => true,
'settings_one_error' => form_error('settings_one'),
'settings_two_error' => form_error('settings_two'),
'settings_three_error' => form_error('settings_three'),
'csrf_token' => $csrf_token
);
}
echo json_encode($data);
}
public function upload_ico()
{
$csrf_token = $this->security->get_csrf_hash();
$favicon_upload_path = './upload/';
if (isset($_FILES['favicon_image']['name'])) {
$config['upload_path'] = $favicon_upload_path;
$config['allowed_types'] = 'ico';
$this->load->library('upload', $config);
if (!$this->upload->do_upload('favicon_image')) {
$data = array(
'error' => true,
'message' => $this->upload->display_errors(),
'csrf_token' => $csrf_token
);
} else {
$data = array(
'success' => true,
'message' => 'Favicon uploaded.',
'csrf_token' => $csrf_token
);
}
} else {
$data = array(
'error' => true,
'message' => 'No file selected.',
'csrf_token' => $csrf_token
);
}
echo json_encode($data);
}
Config.php
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_token';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array(
'admin/settings'
);