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
Related
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
I'm developing a web where you can 'add friend' another user, like facebook does. For now, I put a link, and when you click on it, an AJAX call is done.
Before persist the new friendship, I check if request is Ajax, but I want to go further and add more security. A page can have more than 10 links (posible requests), so... I don't know if I need only one token, or a token per link.
Another doubt is.... How to generate a token and check if is valid using Symfony? Focused on how generate the token on the initial controller, and then, how to validate on the addFriend controller (that receive the ajax call).
I tried to use this to generate a token:
http://api.symfony.com/3.1/Symfony/Component/Security/Csrf/TokenGenerator/TokenGeneratorInterface.html
And then this to check the token:
https://symfony.com/doc/current/controller/csrf_token_validation.html
But always return that the token is not valid.
Finally I find a workaround for my problem.
As #yceruto commented, is possible to generate a csrf token without any form, see this: http://symfony.com/doc/current/reference/twig_reference.html#csrf-token
With this, I can create my links on TWIG on the following way:
<a data-id="{{ user.id }}" class="card-link clickable sendFriendRequest" data-token="{{ csrf_token(user.id) }}">ADD_FRIEND</a>
Then, I do an AJAX call like this:
$('.elementsList').on('click','.sendFriendRequest', function () {
var userId = $(this).data('id');
var token = $(this).data('token');
$.post('/users/sendFriendRequest/'+userId, {token: token}
).done(function (response) {
//Some code here
}).fail(function (response) {
});
});
Finally, you check if the token is valid on your contoller using the following code:
$token = $request->request->get('token');
$isValidToken = $this->isCsrfTokenValid($townId, $token);
Thanks!
PJAX's documentation states that Github uses $.pjax.submit() in submitting a Gist form. A desired feature of an ajax form submission which Github nicely implements is that the URL redirects from the form's action to a newly created URL (in this case, one containing the newly server-side created gist ID).
For example, from this:
https://gist.github.com/gists // form action
to this:
https://gist.github.com/tim-peterson/5019589 //assume this ID is generated server side
I've gotten this to work similarly on my site (i.e., the page itself redirects to the equivalent of https://gist.github.com/tim-peterson/5019589) but I can't redirect the URL (it stays like https://gist.github.com/gists).
Is this entirely a server-side issue (setting headers?) or is there something in pjax that I'm missing? I'm using a version of pjax I downloaded today so it can't be that i'm using a buggier version of pjax.
Did you find any solution to this?
I had the similar issue.
Basically you need to set X-PJAX-URL property in response header.
It's value should be same as Request URL.
In Laravel, I did it like this...
App::after(function($request, $response)
{
$response->headers->set('X-PJAX-URL', $request->getRequestUri());
});
There may be a more-elegant / proper way to do this with $.pjax.submit(), but this is the solution I've been using without any problems. I serialize the form inputs prior to submission, then POST them to my server. Then check the reply from the server to make sure I don't need to prevent the user from going forward before calling my PJAX to continue.
$("#save").click(function(e) {
e.preventDefault();
dataString = $("#form").serialize();
$.ajax({
type: "POST",
url: $("#form").attr("action"),
data: dataString,
dataType: "json",
success: function(data)
{
// Handle the server response (display errors if necessary)
$.pjax({
timeout: 2000,
url: data.url,
container: '#main-content'
});
}
});
});
It happens if response HTML doesn't contain initial PJAX container
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.
In my app, a user must be signed in to submit form info.
After a user clicks on the form submit button, my jQuery checks if a user is signed in.
If not signed in, then an error message pops up, requesting sign in/up.
I can now successfully stop the default action (submit).
However, how do I also allow the default action if the user is already signed in?
With my current code, the default action is also blocked if the user is signed in.
Here's my code:
jQuery('.member-only').click(function(event) {
var $element = jQuery(this);
var SignedIn;
jQuery.ajax({
type: 'POST',
url: '/ajax/member',
dataType: 'json',
success: function(data) {
var $err = jQuery('<div></div>')
.addClass('member-check')
.html(data.msg)
.css('left', $element.position().left);
SignedIn = data.SignedIn;
if (!(data.SignedIn)) { // not signed in
$element.after($err);
$err.fadeIn('slow');
return false;
}
}
});
jQuery('.member-check').live('click', function() {
jQuery(this).fadeOut('slow', function() {jQuery(this).remove(); });
});
if (!SignedIn) {
event.preventDefault();
event.stopImmediatePropagation();
return false; // block default submit
}
});
Thanks.
You need to let your JS function return false; to block the event's default action.
However, this doesn't cover users who have JS disabled or are capable to spoof it. So you should handle this gracefully in the server side as well :)
Update: As per your update, please add
alert(typeof Signedin);
alert(Signedin);
right before if(!Signedin) and tell what you get for both cases. It might be of the wrong type and/or value which is causing that you're always entering the if block and thus always returning false.
For example, a type of undefined will always cause !Signedin to evaluate true. You'd like it to be a boolean type all the time with values true or false.
This is a Moo question. Your not loged in user should have never seen a form that he can't submit in the first place.
Fix your PHP so as to not write a form that a non-logged in user can't submit.
$(form_id).submit(function(){ //triggered when user submits form
var signed_in = check_if_user_is_signed_in(); //checking
if(signed_in){ //singed in
//Do stuff
return true; //submit form
} else{ //user not signed in
//Do stuff
return false; //prevent form from being submitted
}
})
See if there is any user logged in . keep a flag for it. If flag is not set just disable the submit button . or just set the form action part using jquery only if flag is set.
Use event.preventDefault(); in your event handler. Return false only works in some cases (it's more of a workaround).
http://api.jquery.com/event.preventDefault/
https://developer.mozilla.org/en/DOM/event.preventDefault
I would also add an ajax request to check if user is logged in if your website often is opened in multiple windows.
Please review my modifications to your code. Let me know the datatype of the returned data.SignedIn; I added a console.log to return it to firebug.
My example takes action on the document being ready, as opposed to waiting for the user to interact, thus preventing the usability problem of showing the user that an ansynchronous call is happening in the background (the ajax spinner):
$(document).ready(function($){
var memberStatus;
jQuery.ajax({
type: 'POST',
url: '/ajax/member',
dataType: 'json',
success: function(data) {
console.log(data.SignedIn) //I need to know what format this is returned (Boolean: True/False?)
memberStatus = data.SignedIn;
}
});
if (memberStatus) { //if true, that means they are a member
//draw member box
} else {
$("#submitButtonID").attr('disabled',true);
}
});