I have a form which when submitted checks for a CSRF token and validates it. I'm trying to have the same security when I submit the form with ajax. But the ajax request doesn't submit the form itself, it just sends the data to the url with a post request. What would happen if the ajax request submitted this CSRF token with the request. On the server I will then check if the CSRF token. Does this compromise my form security in any way? Can this cause the ajax submission way to be exploited by others in some way?
POSTing to a server and including the token should be just as secure as doing it via the form; it is just another way of doing the same thing. Here's an example from an application I'm working on:
var getCookie = function(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
};
var args = {
_xsrf : getCookie("_xsrf"),
// other args added
};
$.ajax({
url : "/ajaxhandler",
data : args,
type : "post",
dataType : "json",
// .. the rest as usual
});
Yes that should be secure. As long as JavaScript running on another domain can't read the token. This can happen, for instance this JSON abuse used against gmail.
Related
I can't understand why I cannot do more than one request without refreshing page.
Any request is working fine, but If I need to execute another request (don't mind if it's the same or not)... I can't! I need to refresh the page to do a new request, otherwise, I'll get error 403.
All the code is working, just, any request is finishing with refreshing the page. I don't like that because I consider it's not professional.
What do I need to change in codeigniter to allow more than one request without refreshing the page?
update from comment: I use csrf protection and I don't want to disable it.
#Vickel this is one of the requests (all of them is almost the same, just url and data)... there's no form.
function set_tracking(order_id)
{
$.ajax({
type: "POST",
data: {
order_id: order_id
},
url: trackingURL,
beforeSend: function() {
$('.lds-default').removeClass('hidden');
},
success: function (response) {
console.log(response);
if(response.error)
{
show_form_errors(lang['productions_error_not_set_tracking']);
} else {
$('#set_tracking .modal-title').html(lang['productions_set_tracking_title']);
$('#set_tracking').modal('show');
$('#set_tracking #order_id').val(order_id);
}
},
error: function (event) {
if (event.status !== 401) {
show_form_errors(lang['companies_error_deleting_segment']);
}
},
complete: function() {
$('#confirm_message').modal('hide');
$('.lds-default').addClass('hidden');
}
});
}
You're encountering an expired CSRF token. There's multiple ways around this (each with different complexity and security levels)
1.- Disable CSRF altogether (not recommended unless all your forms live within a secure area where everyone is logged in and there's no way to get a cross-domain request, which is unlikely.
2.- Define exceptions for the CSRF functionality. In application/config/config.php you'll find an array called $config['csrf_exclude_uris'] where you can add all the controller/method pairs for which you wish the CSRF checks to not be enforced
3.- Disable CSRF regeneration. In application/config/config.php you could set $config['csrf_regenerate'] to false. This will prevent the CSRF token to be regenerated after each request, which would allow you to make more than one submission without being blocked by the CSRF check
4.- Manually get a regenerated token after the first submission and pass it along with the second submission. This is the most secure way to address your issue, but the most complex. You can read about this in depth in the Codeigniter's Security Class documentation here
in order to manually regenerate the CSRF token you can do the following:
create an hidden input field in your view, which stores your csrf token
in your controler, you create a new token and send it back with your ajax response
in your ajax success function, you update your hidden input field
now you are ready for sending a new request
view:
<?php
$csrf = array(
'name' => $this->security->get_csrf_token_name(),
'hash' => $this->security->get_csrf_hash()
);
?>
<input type="hidden" name="<?=$csrf['name'];?>" value="<?=$csrf['hash'];?>" />
javascript:
tn='<?php echo $this->security->get_csrf_token_name(); ?>';
th=$('input[name="'+tn+'"]').val();
csfrData={tn:th};
$.ajax({
type: "POST",
dataType:json,
data: {
order_id: order_id,
csrf:csfrData // send current token
},
//etc...
success: function (response) {
// update hidden input field with new token
$('input[name="'+response.csrf.csrf_name+'"]').val(response.csrf.csrf_hash)
// etc
}
})
controller:
function your_tracking_url(){
$data['yourdata']=array() // whatever you return
$data['csrf']=$this->get_csrf();
echo json_encode($data);
}
function get_csrf(){
// creating a new token
$csrf=array('csrf_name'=>$this->security->get_csrf_token_name(),'csrf_hash'=>$this->security->get_csrf_hash());
return $csrf;
}
you might need to adapt this a little bit, but it shows the concept how manual csrf token regeneration works
Below is my code and how i submit my data using the Ajax. On first submit, the data is posted successfully, however, when i try again, it fails which i suspect is from an invalid csrf since a new token may be generated. How can i solve this problem ?
$('#icon').on('click', '#test', function() {
var ids = $(this).data('id');
var csrfName = '<?php echo $this->security->get_csrf_token_name(); ?>',
csrfHash = '<?php echo $this->security->get_csrf_hash(); ?>';
var dataJson = { [csrfName]: csrfHash, ids: ids };
$.ajax({
url: '<?php echo base_url('client/data'); ?>',
type: 'POST',
data: dataJson,
}).done(function (result) {
});
});
I have same problem and i solve this by refreshing csrf token. New csrf token get in ajax response form server and replace it old token which is store in form hidden field and when you submit again use the new token.It solve my problem hopes your problem also fixed by doing this, for more use this link https://codeigniter.com/user_guide/libraries/security.html
The solution that worked for me when $config['csrf_regenerate'] = TRUE 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
So, here is ajax setup that I have for my wordpress site.
Page_1.php:
<?php echo '<div class="button" data-post_date="'.$rh_post_date.'" data-post_author_id="' .$rh_author_id. '" data-post_id="' .$id. '">' ;?>
custom_js.js:
jQuery('.button').click(function(e) {
e.preventDefault();
var indiv_id = jQuery(this).data("post_id");
var indiv_post_author = jQuery(this).data("post_author_id");
var indiv_date = jQuery(this).data("post_date");
jQuery.ajax({
type: "GET",
url: upload_image.ajax_url,
dataType: 'html',
data: ({ action: 'rhmp_indi_form', post_id: indiv_id , post_author_id: post_author, post_date_id: indiv_date}),
success: function(data){
jQuery('#rh_pop').html(data);
},
error: function(data)
{
alert("Error!");
return false;
}
});
});
Page_2.php:
<div id="rh_pop">
<?php
$page_2_post_id = $_REQUEST['post_id'];
$page_2_post_author_id = $_REQUEST['post_author_id'];
$page_2_post_date_id = $_REQUEST['post_date_id'];
?>
</div>
As you can see, when the button in the page_1.php is clicked, the data become variables in custom_js.js. These variables are then sent to the page_2.
Now, I know that this is not secure at all and can be hacked easily.
So, how do I send data such as data-post_date or post_author_id to another page via ajax using php?
First of Ajax is a technique that is used in javascript to send a httprequest to the php server. The server handles that request and sends back its result. Its the same as requesting a page normally but then its without reloading the page.
Creating variables in javascript with php for use with ajax doesn't mean that php is the one doing anything ajax related. Hope this clears up a missunderstand.
More info on this: https://en.wikipedia.org/wiki/Ajax_%28programming%29
Sending data to the server is never safe. You can make help features like you need to fill in this field with a name and go on. Or maybe even use a disable the submit button if there aren't 3 or more numbers in a field but this is all to help the user and to make it unlikely that the server will get requests that will never be valid anyway.
This is the reason why server side validation is always needed and client side validation is more of a user friendly thing and making sure the server doesn't get requests that can be detected from the client side already.
I use a 3rd party form processor which is zapier.com. The issue is, I need a way to redirect the user after submitting data to the 3rd party form processor. Zapier.com accepts post, get, and put submissions.
I was thinking of:
Client submits form
My php form-processor captures the data
My php form-processor then Submits the data via POST or GET to the 3rd party form processor
My php form-processor then redirects the user to a thank you page.
I might be over thinking this, but the only way I see of doing this is making a form that posts data that has been posted to it. Otherwise the form will just send my user to the 3rd party processor without redirecting them to whatever page I choose. The 3rd party form processor doesn't have a way of me using custom redirects.
Using javascript and Ajax it can be done like this (with jQuery):
$("#idOfTheForm").submit(function(e) {
e.preventDefault();
$.ajax({
method : "post",
url : this.action,
data : $(this).serialize(),
success : function() {
window.location = "yourUrlOfThanks.html";
},
error : function() {
alert("Something went bad");
}
});
});
So basically it is: sent a post request to the action url of this form, and once it throws an 200 code (found and everything went ok) then redirect to my page. If soemthing went wrong, then the server will throw an 40* status code and the script will go into error function.
You can use Guzzle to proxy the user request.
$client = new Guzzle\Http\Client();
$request = $client->post('/3rd.party.php')
->addPostField('user_field_1', $_POST['user_field_1'])
->addPostField('user_field_2', $_POST['user_field_2']);
$response = $request->send();
if ($response->isSuccessful()) {
//show message
}
The downside is that you can't be 100% sure that the form submission was indeed successful. In order to achieve that you could scrap the $request->getBody() and check if a known success message is present.
I am wondering if this is a secure way to process credit card information. The site uses PHP and IS using an SSL Certifacate, but instead of submitting a form and getting $_POST variables. I want to try to use JQUERY AJAX and communicate with the user if their information was approved or not. But I am worried about if this method is secure. And example of my code is below.
$.ajax({
type: "POST",
url: "ajax_process_credit_card.php",
data: { cardnumber : cardnumber , cardexpmonth : cardexpmonth, cardexpyear: cardexpyear, chargetotal: chargetotal, ordertype: ordertype },
success: function(msg){
$("#status").ajaxComplete(function(event, request, settings){
if(msg == 'APPROVED'){
complete_registration();
}
else // ERROR?
{
var error_message= msg;
$('#error_message').html(error_message);
}
});
}
});
Would this be ok?
As long as you use HTTPS it doesn't really matter. Just ensure you use POST so the data never appears in an access log.
Note that you should get PCI certified if you are dealing with credit card data. Or maybe let another company deal with it and avoid all the trouble.
The AJAX methods are actually doing an HTTP POST behind the scenes, as your type is set to POST.
Be sure to do all connections over HTTPS.
If you are posting to the same site and you are posting to https, not to the http equivalent then I don't see how it could be any less secure.
Assuming that the URL of your page was server over HTTPS and that the URL your AJAX query is requesting is also server over HTTPS the communication should be just as secure as it would be if you were using a normal post0back to that same URL.
Just make sure that the URL you're posting to is server over HTTPS. On the server side you could also check that the request was made over HTTPS and reject any requests that were not..