I'm working on a CodeIgniter application where I'm using CSRF protection with AJAX form submissions. I have $config['csrf_regenerate'] = TRUE; enabled in my configuration, but I'm encountering a 403 error on repeated form submissions.
Problem:
I'm looking for guidance on how to best handle CSRF protection and token regeneration in my scenario.
Form
<form id="add_category_form" method="post">
<h2>Add a Category</h2>
<ul>
<li>
<input type="text" name="category_name" required>
<label>Category Name</label>
</li>
<li>
<textarea name="description" required></textarea>
<label>Description</label>
</li>
<!-- FIXME: center the content of this last li tag -->
<!-- <li>
<label>Upload Images (5 Max)</label>
<input type="file" name="image" accept="image/*">
</li>
</ul> -->
<button type="button" data-dismiss="modal" aria-label="Close">Cancel</button>
<button type="submit">Save</button>
</form>
Jquery
<script>
$(document).ready(function() {
$("#add_category_form").on("submit", function(e) {
e.preventDefault();
let csrfTokenName = '<?= $this->security->get_csrf_token_name(); ?>';
let csrfHash = '<?= $this->security->get_csrf_hash(); ?>'; // Assuming you have these values in your view
let form_data = $(this).serialize() + "&" + csrfTokenName + "=" + csrfHash;
$.post("<?= base_url('CategoriesController/process_add_category'); ?>", form_data, function(response) {
console.log(response);
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.error("AJAX Error:", textStatus, errorThrown);
});
return false;
});
});
</script>
Controller method
public function process_add_category() {
$category_name = $this->input->post("category_name");
echo $category_name;
}
This problem is now solved. I resolve this by setting the newly returned CSRF token from the server to the form's CSRF token so that everytime I submit a form, my form's token and server's token will always same and avoid CSRF token mismatch.
Form
<form id="add_category_form" action="<?= base_url("CategoriesController/process_add_category") ?>" method="post">
<input type="hidden" name="<?= $this->security->get_csrf_token_name() ?>" value="<?= $this->security->get_csrf_hash() ?>" />
<h2>Add a Category</h2>
<ul>
<li>
<input type="text" name="category_name" required>
<label>Category Name</label>
</li>
<li>
<textarea name="description" required></textarea>
<label>Description</label>
</li>
<label>Upload Images (5 Max)</label>
<!-- <ul>
<li><button type="button" class="upload_image"></button></li>
</ul> -->
<input type="file" name="image" accept="image/*">
</ul>
<!-- FIXME: center the content of this last li tag -->
<button type="button" data-dismiss="modal" aria-label="Close">Cancel</button>
<button type="submit">Save</button>
</form>
JavaScript
$(document).ready(function() {
$("#add_category_form").submit(function(e) {
e.preventDefault();
let url = $(this).attr("action");
let formData = $(this).serialize();
console.log(formData);
$.post(url, formData, function(response) {
console.log(response);
/* Set the returned newly created hash and name to the form's token name and value*/
csrfName = response.csrfName;
$("input[name='<?= $this->security->get_csrf_token_name() ?>']").val(response.newCsrfToken);
}, "json");
return false;
});
});
Controller
public function process_add_category()
{
$response = array(
"message" => "Added category successfully!",
"csrfName" => $this->security->get_csrf_token_name(),
"newCsrfToken" => $this->security->get_csrf_hash()
);
echo json_encode($response);
}