How to add CSRF protection to AJAX call on Symfony3? - php

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!

Related

Avoid error 403 in codeigniter on a second POST request

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

PHP: Send form data to php script which then posts data to another form processor like zapier.com?

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.

ExtJS: How to store a user's login information only once?

I'm using Ext.Ajax.request() to hit a PHP page that gives me some user specific info as a part of a login process. Basically I want to store some variables (cookie, session information, etc) in ExtJS. I created a model with the needed fields and a store to keep it. Even though the authentication is done only once I want to ensure that on a given time there is only one copy of the user's info. I want to use that info to hit PHP again without having to authenticate again (cookies).
Here's how I fetch it and store it:
Ext.Ajax.request({
url: 'api/get.php',
params: {
user : usr,
pass : pass
},
success: function(response)
{
var text = response.responseText;
var jtext = Ext.JSON.decode(text);
var login = jtext.loginData;
model.set('username', login.username);
model.set('cookie', login.cookie);
model.set('password', login.password); // doing this feels soo wrong!
store.insert(0, model);
store.sync();
console.log(store.getCount());
}
});
What is the better way of doing it? Since I'm using an external (to the PHP) service currently I only check if the user/pass is correct and if it is, I pass it from the client-side to the PHP functions that do whatever they have to do with the external service, authenticating each time with them. Alternatively I can pass the cookie each time to the PHP that in turn passes it down. I can also map JS<->PHP cookies to PHP<->service cookies and that would be even better.
Cheers!
Ext.util.Cookies.set('myObj',Ext.encode(obj))
Use something like this and when you want to use it
id = (Ext.decode(Ext.util.Cookies.get('myObj'))).id

stay connected with twilio call on page refresh

i need help to stay connected with the call during conversation. i have used the following code given on twilio Doc.
PHP Part is here
$token = new Services_Twilio_Capability(TW_ID, TW_TOKEN);
$token->allowClientIncoming($_SESSION['emp_id']);
JS Part:
Twilio.Device.setup("<?php echo $token->generateToken();?>");
$("#call").click(function() {
params = { "tocall" : $('#tocall').val()};
connection = Twilio.Device.connect(params);
});
$("#hangup").click(function() {
Twilio.Device.disconnectAll();
});
Twilio.Device.ready(function (device) {
$('#status').text('Ready');
console.log(Twilio.Device.status());
});
Twilio.Device.incoming(function (conn) {
startCall(conn);
});
on the page load the the Twilio.Device.ready() function make the user login the receive the calls, but the the user refresh the page the call went disconnected, or if user trying to do something like updating the information of client during the conversation getting the call disconnected. Hope you understand.
But now don't have any idea that how to rid this issue. One option i have to use the hash value on the url something like #status.
Please help me to give any alternate solution or modifying this code..
You can use the session for this...
store the token value in the session.
$_SESSION['token']=$your_token;
access like this
<?php echo $_SESSION['token'];?>
There are a few different options you could take.
Store the call state on the server, via a POST request to your server when the call connects. Then when the client refreshes the page, push an incoming (the existing) call to the client.
Use HTML5 localStorage or similar to store the call state on the client. Your JS should check for existence of a call in localStorage when the page loads. If a call exists in this DB, then connect to the same endpoints as before.

How to deal with session timeouts in AJAX requests

I'm sure you're all familiar with the voting systems that use AJAX (Um... look right over there <----)
I have something similar and when you vote up or down it uses AJAX to request the new value from votes.php. The problem is that I am using a session to get the userid so a person can only vote once. What happens if they sit on the page for an hour and then vote so the session is no longer there? What would be a good way of handling this situation? Should I redirect their page to the login screen? If so, how can I do that from the votes.php page that is being referenced by the AJAX request? Am I overlooking a good way of handling this situation? Any advice would be helpful.
Consider returning an http status of 401, and a JSON object detailing the reason. If you're using jQuery, that'll drop you to the error() callback, which you can then parse your object.
$.ajax({
data: {},
dataType: 'html',
success: function(data) {
// do whatever here
},
type: 'POST',
url: 'myserver.com',
error: function(XMLHttpRequest, textStatus, errorThrown) {
// XMLHttpRequest.responseText has your json string
// XMLHttpRequest.status has the 401 status code
if (XMLHttpRequest.status === 401) {
location.href = 'login.php';
}
}
});
I'm not familiar with PHP anymore, but this should work for just about any environment. You may have to suppress any automatic login form redirection though. In asp.net mvc the framework will see the 401 and push the default login form back, with a status of 200.
You should only store a link to the users identity in the session. Use sessions to identify a user as x and then get user x's information from the database.
If your problem is with users sessions timing out then you should reconsider how you're using your sessions. Perhaps make them last until the browser closes? If you really want to make them a duration, then perhaps ping the server in intervals to keep the session alive.
Decide in your php script whether or not the user should be able to vote. If the session isn't set, or if they have already voted, return a message that you can identify with on the client side. If they already voted perhaps return "voted":"true" in a JSON object. Use JS to parse this object and understand what it means, taking the appropriate action. If the session isn't set, perhaps return "session_set":"false", and then make javascript redirect with a window.location = "login.php" etc.
Only increment the counter for the user on a successful return of a counted vote.
This is an old thread, but I wanted to share my solution that is working really well.
In my framework the system redirects the user to the login form any time they try to access a page and the session has timed out or is not valid.
I added to the top of the login form the following html comment:
<!--LOGINFORM-->
I created a wrapper for jQuery's $.ajax function which checks for this string on every request, and if it is there it shows a dialog popup saying that their session has timed out.
You can use this by just calling:
ajax.get('http://someurl.com', function(data){
//Do stuff
});
Hope it helps someone.
var ajax = {
check_login : function(resp){
if (resp.substring(0, 16) === "<!--LOGINFORM-->"){
// Show a popup or redirect them to login page!
return true;
}
return false;
},
get : function(url, success){
if (typeof data =='undefined'){
data = null;
}
$.ajax({
url: url,
type : 'GET',
success : function(resp){
if (!ajax.check_login(resp)) {
success(resp);
}
},
});
}
};
You structure the Javascript code that makes the Ajax request to accept a special result (say, -1 where a >=0 number would normally be, such as, a count of votes) to mean "sorry bub, you're timed out" and redirect to the re-login page (which can take as an optional parameter a message explaining to the user they timed out, &c).
You could create a javascript function that could ping the server every 10 minutes via something like
setTimeout("Ping()", 60000);
If you want to navigate the user to the login page if they connect with a faulty session then I would first verify the session and if it fails send a
header("Location: ...");
http://ca2.php.net/manual/en/function.header.php
From a user perspective, the best solution is to pop up a message and login form, saying something like "You are not logged in or your session timed out". Digg does this very well.
As for the actual AJAX implementation, swilliams' 401 suggestion is solid. Alternatively, you can simply return a specific string on failure.

Categories