Regenerate CRSF token codeigniter on submit Ajax - codeigniter

Hi I am looking for the process of regeneration of csrf token in codeigniter when ever a form is submitted using ajax. I want the token to be regenerated without page refresh. Is there any way to do that.

There are two solutions I use at different times depending on the situation.
1. Slightly messy way but recommended
Get the token name and hash value in your controller and set it somewhere on your page as a data field (wherever you choose). For instance
// get the data and pass it to your view
$token_name = $this->security->get_csrf_token_name();
$token_hash = $this->security->get_csrf_hash();
// in your view file, load it into a div for instance
<div id="my_div" data-token="<?php echo $token_name; ?>" data-hash="<?php echo $token_name; ?>"
Now in your js ajax code, you can just read the data values in "my_div" to get the right data for your ajax call.
It is made much easier if you have a genuine form on your page, in which case rather than using some div, just do not use form_open on the form, but instead create the hidden form field yourself, so you can read it easily via js.
<input type="hidden" id="my_data" name="<?=$csrf['name'];?>" value="<?=$csrf['hash'];?>" />
This is the important bit: Of course after sending post data, you need to refresh the token hash value (in your form input field or a div data, however you have chosen to do it). Write a js function called 'refresh_csrf_data' and use 'GET' to get the data and update the fields. This function can then be called whenever you have done an ajax post.
So every ajax call reads the token data, does the call, then refreshes the token data ready for the next call.
2. Easy but less secure
Alternatively, you can disable CSRF for your ajax calls by using the
$config['csrf_exclude_uris'] = array('controller/method');
in the config file for CSRF settings.
3. Even easier but also less secure and I do not use it
Finally, you could turn off regenerating CSRF hash on every submission
$config['csrf_regenerate'] = FALSE;
But, do so with caution. This can open you up to certain types of attacks.
The answer that is best for you depends entirely on the type of page, the usage, if users are logged in at the time or not, is it mission critical stuff or minor stuff, is it financial etc.
Nothing is entirely secure, so it is a compromise sometimes. Personally I would do it with CSRF on full regenerate, no exceptions in the URI's, and reload the token and hash data whenever I needed to. It seems complicated and it is to explain, but once you have done it once, it is genuinely easy to do again and again whenever you need it, and your site will be far more secure than simply avoiding the issue with the other options.

The solution that worked for me is that for subsequent ajax post when CSRF is enabled for every request is to make a GET request in AJAX Success when request fails because token has expired. Then have a hidden field that continue to be updated with latest token and if at the time of making request it has expired you make a GET REQUEST to fetch latest TOKEN and then evoke click event on function that submits form or function making POST request which means the function has to be passed "this" or ID as part of parameter.This makes the user not to realize the process of renewing token in the background

I find using the form helper function works better with Codeigniter
CSRF and stops throwing the CSRF error If you use normal html input
will keep throwing CSRF error.
http://www.codeigniter.com/user_guide/helpers/form_helper.html#form_input
http://www.codeigniter.com/user_guide/helpers/form_helper.html#form_password
Here is a example AJAX
<?php echo form_open('controller/example', array('id' => 'form-login'));?>
<?php
$username_array = array(
'name' => 'username',
'id' => 'username',
'class' => ''
);
echo form_input($username_array);
?>
<?php
$password_array = array(
'name' => 'password',
'id' => 'password',
'class' => ''
);
echo form_password($password_array);
?>
<?php
$submit_array = array(
'name' => 'submit',
'id' => 'submit',
'class' => '',
'value' => 'Submit',
'type' => 'submit'
);
echo form_input($submit_array);
?>
<?php echo form_close();?>
<script type="text/javascript">
$('#submit').click(function(e){
e.preventDefault();
var post_data = {
'username' : $('#username').val(),
'password' : $('#password').val(),
'<?php echo $token_name; ?>' : '<?php echo $token_hash; ?>'
};
$.ajax
({
type: 'post',
url: "<?php echo base_url('example/');?>",
data: post_data,
dataType: 'json',
success: function(response)
{
if (response['success'] == true) {
// Success
} else {
// Error
}
}
});
});
</script>
Example
public function index() {
$data['token_name'] = $this->security->get_csrf_token_name();
$data['token_hash'] = $this->security->get_csrf_hash();
$this->load->view('login_view', $data);
}
public function example() {
$data = array('success' => false, 'messages' => array());
$this->form_validation->set_rules('username', 'username', 'required');
$this->form_validation->set_rules('password', 'password', 'required');
if ($this->form_validation->run() == false) {
foreach ($_POST as $key => $value) {
$data['messages'][$key] = form_error($key);
}
} else {
$data['success'] = true;
}
echo json_encode($data);
}

if it's not on session then you controller will be like this
public function save_date() // or whatever function you like
{
$regen_token = $this->security->get_csrf_hash();
$data = array(
"data" => $this->input->post('datas'),
);
$insert = $this->w_m->save($data);
echo json_encode(array("regen_token" => $regen_token));
}
in your ajax will be look like this:
$.ajax({
url: "your url",
type: "POST",
data: { your data },
dataType: "JSON",
success: function(data)
{
$("name or id of your csrf").val(JSON.stringify(data.regen_token)).trigger("change"); // this will be the function that every post you'll request and it automatically change the value of your csrf without refreshing the page.
},
error: function(errorThrown)
{
console.log(errorThrown);
}
});

in the callback add an array of the csrf hash.

Related

Laravel ajax is not returning success data

I am trying to achieve a functionality, If I select a category in dropdown, then in next dropdown, subcategory of that chooces category will appear. I wrote below ajax in script tag for it.
$("#category").change(function(e){
e.preventDefault();
var category = $("#category").val();
$.ajax({
type:'POST',
url:"{{ route('ajaxRequest.post') }}",
data:{category:category},
success:function(data){
alert(data.success);
$("#subcategory").replaceWith(data.subcats);
}
});
});
Here is my route setup for the same
Route::get('add_product', [dashboardController:: class, 'addProduct']);
Route::post('add_product', [dashboardController::class, 'getSubCategories'])->name('ajaxRequest.post');
This is my controller function
function getSubCategories(Request $request){
//$input = $request->all();
$subCategoryList = DB::table('ajax_categories')->where('pid', $request->post('category'))->get();
$sub = '<option desabled selected>Choose sub category</option>';
foreach($subCategoryList as $subCategory):
$sub .= '<option value="'.$subCategory->id.'">'.$subCategory->category.'</option>';
endforeach;
return response()->json(array(
'success' => 'Success',
'subcats' => $sub
));
}
Everything seems fine, I am not getting what causing it to be fail.
Screenshot of network tab
On clicking on checkbox, I got this in reponse
You are echoing result before returning response so ajax is not able to parse json properly.And other is csrf token not passed properly.
In ajax you can pass csrf token like below
data:{_token: "{{ csrf_token() }}",category:category},
instead of appending in controller better do like this
function getSubCategories(Request $request){
$subCategoryList = DB::table('ajax_categories')->where('pid', $request->post('category'))->get();
$view=(string)view('dropdown',['subCategoryList'=>$subCategoryList])
return response()->json(array(
'success' => 'Success',
'subcats' =>$view
));
}
in your view dropdown.blade.php you can
#if(isset($subCategoryList)&&count((array)$subCategoryList))
#foreach($subCategoryList as $key=>$value)
<option value="{{$subCategory->id}}">{{$subCategory->category}}</option>
#endforeach
#endif

ajax not getting any response from cakephp code

I am using CakePHP 2.9 to send data on the URL using ajax and get the related response.
I tried may method to get the response, I also want to know why this //URL:'/Pages/dropdownbox/'+id is not working.
bellow are ajax code which I wrote in the index.ctp.
$("#certificatedetail").on('change',function() {
var id = 'subcribe';
$("#usertype").find('option').remove();
$("#certificateclass").find('option').remove();
$("#certificatetyp").find('option').remove();
if (id) {
$.ajax({
type: 'POST',
url:'<?= Router::url(array('controller' => 'Pages', 'action' => 'dropdownbox','id')); ?>',
//url:'/Pages/dropdownbox/'+id,
dataType:'json',
cache: false,
async:true,
success: function(html)
{
$('<option>').val('').text('select').appendTo($("#usertype"));
$('<option>').val('').text('select').appendTo($("#certificateclass"));
$('<option>').val('').text('Select').appendTo($("#certificatetyp"));
$.each(html, function(key, value)
{
$('<option>').val(key).text(value).appendTo($("#usertype"));
});
}
});
}
});
I have written this controller code in PagesController,PHP and I declared the dropdownbox in AppController.php
public function dropdownbox($id = null)
{
Configure::write('debug', 0);
$this->layout = null;
$this->autoRender = false;
$category = array();
switch ($id)
{
case $id == "subcribe":
if ($id == 'subcribe') {
$category = array(
'individual' => 'Individual',
'organization'=>'Organization',
'organizationgovt' => 'Organization-Govt',
'organizationbank' => 'Organization-Bank'
);
break;
}
}
}
/ bellow is the code where I specify the dropdownbox function in AppController.php
public function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow(
'login','add','index','contact','dropdownbox',
'cityres','stateres','sectorres','productres',
'project','service','about','apply','tender',
'decregistration','search','searchresult',
'tenderdetails'
);
}
You are generating the URL on your server, and using the string literal 'id' in it. The JavaScript id variable is never referenced. What you probably want is:
url:'<?= Router::url(array('controller' => 'Pages', 'action' => 'dropdownbox')); ?>' + id,
You are not returning any response from the controller. Do few things to debug it
Check in Browser's Network tab whether the called URL is correct or not.
Check the parameters are correct or not.
This is how it looks in Firefox Developer Edition
If URL is correct. Add below code in the dropdownbox() method. (Before the closing of the method)
echo json_encode($category);
Check Response Tab in the Network tab. (Example image above).
Also, console the response in the javascript code. Maybe you will be getting some other response which is not JSON.
success: function(html) {
console.log(html);
...
}
Hope it helps.

Yii2: ajax form validation on an ajax submitted form

I'm wondering if any Yii2 experts can help me understand how best to work with ajax forms combined with Yii ajax validation. I think I can explain the issue without taking you through all of my code.
I am working on a Promo Code entry form where the user enters their promo code into the form, the form is submit via ajax. We then perform a database lookup for the promo code details, validate the code and if the code validates, we want to display the registration form that is hidden on the page.
I have a custom validation function for the form field "code", which is the active field in a model scenario named "register".
class UserCode extends ActiveRecord
{
...
public function scenarios()
{
return [
'register' => ['code'],
];
}
public function rules()
{
return [
[['code'], 'required'],
[['code'], 'validateUserCode', 'on' => ['register']],
];
}
public function validateUserCode($attribute, $params)
{
// perform all my custom logic to determine if the code is valid
if ($code_invalid) {
$this->addError($attribute, 'Sorry, this code is invalid.');
}
}
...
}
Then in the controller, as the Yii2 Guide suggests, I trap this ajax validation with the following code:
public function actionValidate() {
$model = new UserCode(['scenario' => 'register']);
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
// no logic can be run after the above code b/c the form is submit with ajax
// and therefore always trapped in the Yii::$app->request->isAjax conditional
}
The above code all works fine and if I remove focus from the $form->field($model, 'code') field on my form, Yii's ajax validation kicks in and displays my custom error message based off of my custom validation logic.
My challenge arises when I go to submit the form. The form submission is also handled through ajax, and therefore the controller action always returns the result of the ActiveForm::validate($model); because if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) will get apply to both the ajax form validation AND on the form submit.
With the above approach, I am forced to return only the results of the ajax validation and not any json data that I may need for additional client side validation, such as displaying the registration form after a valid use code is submitted through the ajax form.
I realize that I can set 'enableAjaxValidation' => false on the ActiveForm and then return my own json data inside the if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) condition. If I do this, I am able to show the registration form because I have my own json data to work with.
Is there a way to have ajax validation on a form that is submitted with ajax? How could you trap the ajax validation separately from the ajax form submission to handle the two events in different manners?
Any suggestions or alternate approaches are GREATLY appreciated!
You should set up validationUrl with a different URL compared to the URL that you are submitting the form to. In this way you can have the validation function that would validate and return the return ActiveForm::validate($model); and the normal submit form that does something else.
You can read more about validationUrl here:
I have found solution :
Form :
<?php
$form = ActiveForm::begin(['id' => 'form-add-contact', 'enableAjaxValidation' => true, 'validationUrl' => Yii::$app->urlManager->createUrl('contacts/contacts/contact-validate')]);
?>
Submit Via Ajax :
<?php
$script = <<< JS
$(document).ready(function () {
$("#form-add-contact").on('beforeSubmit', function (event) {
event.preventDefault();
var form_data = new FormData($('#form-add-contact')[0]);
$.ajax({
url: $("#form-add-contact").attr('action'),
dataType: 'JSON',
cache: false,
contentType: false,
processData: false,
data: form_data, //$(this).serialize(),
type: 'post',
beforeSend: function() {
},
success: function(response){
toastr.success("",response.message);
},
complete: function() {
},
error: function (data) {
toastr.warning("","There may a error on uploading. Try again later");
}
});
return false;
});
});
JS;
$this->registerJs($script);
?>
Controller :
/*
* CREATE CONTACT FORM AJAX VALIDATION ACTION
*/
public function actionContactValidate() {
$model = new ContactsManagement();
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
$model->company_id = Yii::$app->user->identity->company_id;
$model->created_at = time();
\Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
}
/**
* Quick Add Contact Action
* #param type $id
* #return type
*/
public function actionAddContact() {
$model = new ContactsManagement();
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
$transaction = \Yii::$app->db->beginTransaction();
try {
if ($model->validate()) {
$flag = $model->save(false);
if ($flag == true) {
$transaction->commit();
return Json::encode(array( 'status' => 'success', 'type' => 'success', 'message' => 'Contact created successfully.'));
} else {
$transaction->rollBack();
}
} else {
return Json::encode(array('status' => 'warning', 'type' => 'warning', 'message' => 'Contact can not created.'));
}
} catch (Exception $ex) {
$transaction->rollBack();
}
}
return $this->renderAjax('_add_form', [
'model' => $model,
]);
}

CakePHP AJAX call

I am using CakePHP and this is my first project on this framework. I am going to send the value of an input to UsersController's check_username() action. And fill an element having id na with the string returned by check_username(). So far what I did is:
//in my form
<input type="text" name="data[User][username]" style="width: 60%" required="required" id="username" oninput="check_username(this.value)">
<label style="margin-left: 20px; color: red" id="na">Not Available!</label>
//after the form
<script type="text/javascript">
function check_username(un) {
$.ajax({
type: 'POST',
url: '/oes/users/check_username',
data: {username:un},
cache: false,
dataType: 'HTML',
beforeSend: function(){
$('#na').html('Checking...');
},
success: function (html){
$('#na').val(html);
}
});
}
</script>
//and my check_username() is
public function check_username(){
return 'Test string';
}
But this isn't working. Anybody know why or how to modify it so that it works?
It could be problem with your check_username controller action. CakePHP-way is to use JsonView class to send any data throw XHR (see http://book.cakephp.org/2.0/en/views/json-and-xml-views.html). It allows you to call any action with .json extension (ex.: /oes/users/check_username.json) and get response in serialized JSON format without manual conversion beetween array data and JSON.
This method is recommended for your needs, but not obligated, of course.
Now I think that CakePHP tries to render check_username view, but could not do this because you have not specified or created it. Try to change your action code to something like this:
public function check_username(){
$this->autoRender = false;
echo 'Test string';
}
Also, try not to use such code construction in the future.
CakePHP has a JS Helper to help write aJax functions. The only catch is to include jquery in your head our cake will throw jQuery errors.
Your Form:
<?php
echo $this->Form->create('User', array('default'=>false, 'id'=>'YourForm'));
echo $this->Form->input('username');
echo $this->Form->submit('Check Username');
echo $this->Form->end();
?>
The Ajax Function:
<?php
$data = $this->Js->get('#YourForm')->serializeForm(array('isForm' => true, 'inline' => true));
$this->Js->get('#YourForm')->event(
'submit',
$this->Js->request(
array('action' => 'checkUsername', 'controller' => 'user'),
array(
'update' => '#na',
'data' => $data,
'async' => true,
'dataExpression'=>true,
'method' => 'POST'
)
)
);
echo $this->Js->writeBuffer();
?>
The Function in User Controller
function checkUsername(){
$this->autoRender = false;
//run your query here
if ( $username == true )
echo 'Username is taken';
else
echo 'Username is not taken';
}
There are many examples through google. Here is a good one to visit.

Is updating codeigniter db-session data clientside via ajax possible?

I'm using codeigniter with encrypted sessions in the database and I'm using a twitter bootstrap modal to update some user details in a form.
I use jquery validation on the form and in the submitHandler I post the data via ajax and close the modal.
submitHandler: function (form) {
document.getElementById("edit-profile-submit-button").disabled = true;
$('.modal-ajax-loader').show();
$.ajax({
type: $(form).attr('method'), // 'Post'
url: $(form).attr('action'), // 'profile/edit_basic_details'
data: $(form).serialize(),
success: function(data, status){
$(form).html(data);
$('.modal-ajax-loader').hide();
setTimeout(function() { $('#edit-profile-details').modal('hide'); }, 2000);
},
error: function(data, status) {
$(form).html(data);
}
});
return false;
}
and here is the model function called from the controller with the same name,
function edit_basic_profile() {
$screenname = $this->security->xss_clean($this->input->post('screenname'));
$firstname = $this->security->xss_clean($this->input->post('firstname'));
$lastname = $this->security->xss_clean($this->input->post('lastname'));
$email = $this->security->xss_clean($this->input->post('email'));
$bio = $this->security->xss_clean($this->input->post('bio'));
$data = array(
'screen_name' => $screenname,
'first_name' => $firstname,
'last_name' => $lastname,
'email' => $email,
'bio' => $bio,
);
try{
// Run the update query
$this->db->where('profile_id', $this->session->userdata('profile_id'));
$this->db->update('profiles', $data);
// Let's check if there are any results
if($this->db->affected_rows() == 1)
{
// Setup the session information for the user
$this->session->set_userdata($data);
return true;
}
// If the previous process did not update rows then return false.
error_log("profile_model, edit_basic_profile(): There were no affected rows");
return false;
} catch(PDOExceprion $e) {
error_log("profile_model, edit_basic_profile(): ".$e);
return false;
}
}
I can update the values that changed on the page in the submitHandler also and of course the session on the server is updated in the model.
$("#profile-screenname").html($(screenname).val());
$("#profile-bio").html($(bio).val());
The problem is when I open the modal again it grabs the user details from the session data in the browser cookie and grabs the original data unless the page has been refreshed after the first update.
(form data is loaded like this);
<input type="text" class="input-large" id="firstname" name="firstname" placeholder="First Name" value="<?php echo $this->session->userdata('first_name'); ?>">
"<?php echo $this->session->userdata('first_name'); ?>" on the second time i open the modal before any page refresh loads the old data.
Sure, you just need to call an ajax url which updates/sets new session data:
HTML+JS
---> Ajax call
----> $this->session->set_userdata('key','new-value');
----> session and db updated.
Done.
Also notice and change all these:
$screenname = $this->security->xss_clean($this->input->post('screenname'));
to this:
$screenname = $this->input->post('screenname',true);
which is exactly the same result

Resources