CSRF attack detected when submitting data using ajax - php

I'm trying to submit a form using jquery in symfony 1.4, but CSRF attack detected error pops up each time. This is the code i use to submit the form data:
$.ajax({
type: 'post',
cache: false,
url: $('#assign-form form').attr('action'),
data: (
'activity[id]=' + $('#activity_id').val() +
'&activity[_csrf_token]=' + $('#activity__csrf_token').val() +
'&activity[assigned_to_user_id]=' + $('#activity_assigned_to_user_id').val() +
'&activity[assigned_to_group_id]=' + $('#activity_assigned_to_group_id').val()
)
});
Am i missing something?
Thanks,
Radu.

One thing to look at is whether the Form object that is validating the input is the exact same class as the the one that generated the token. By default, the class is used in the generation of the CSRF token.
If, for example, you had an instance of a subclass of a form used to generate the form html and then you posted to an action that used the ancestor form class, the validation would most likely fail by default.
It's possible to override the token creation if it turns out this is your issue.
Also, have you verified that the value of the token field actually includes a token? A console.log() may help you discover this.
Um...as I look closer at your code, another thing to look at is that you're building a query string to pass in 'data'. Have you tried passing an actual JSON-style object instead?

Usual cause is that the browser is not accepting cookies - have you checked that the cookies are being returned as expected (e.g. iehttpheaders, wireshark, tamperdata)?
Did you configure the secret in settings.yml?
C.

This little issue has driven me mad in the past.
If it's acceptable to you to disable CSRF protection for this particular form (which it often can be), you can add the following to your Form class in /lib/form/... folder:
public function configure ()
$this->disableLocalCSRFProtection();
I believe it's possible to disable CSRF for a particular instance of the form as well if you don't always wish to have it disabled, but I haven't tried this / don't have the code at hand.

Does the session cookie really received it with ajax query ? your session cookie as returned by server should be exactly the same with a plain HTTP request (for instance the first request initiating session management) and with XMLHttpRequest, otherwise you'll get trouble with CSRF.

$('#activity__csrf_token').val()
Did you mean to have a double underscore in that element id?

Related

laravel 4 persisting data ongoing - jquery ajax submit

I'm having ongoing problems with laravel 4.1 sessions and getting unexpected behaviour.
Depending on how I call the controllers method the session either works or doesn't My app makes a call to a POST route - to add items to a cart which is a session. For some reason the session does not get updated.However if I make a call to the same function with a GET request the function works as expected.
My routes.php contains these two routes:
Route::get('testAdd', array('uses' => 'ProductsController#addToCart'));
Route::post('products/addToCart', array('uses' => 'ProductsController#addToCart'));
Both point to the same method
The method is currently this (for testing):
public function addToCart() {
Session::put("addcarttest", "add to cart");
return json_encode(Session::get('addcarttest'));
}
If I call the function with the POST method (with form data) I get the expected result and the contents of the session.
However If I then check for the session (using a profiler) it does not exist. The data did not persist.
If I then call the same method using the GET route, I get the expected result but importantly the session persists.
I thought maybe the POST method deleted sessions however once it exists it stays there - if I use the GET method and the sessin exists if I then try the POST method example again the session remains in place - so the POST method doesnt delete the session.
This is driving me crazy - I've lost a lot of hours over this and can't see why.
Am I missing something over how Laravel handles POST v GET ? Why would two different methods make a difference to underlying functions?
What do I need to do to make the session work correctly with POST?
Update:
I've now tried database driver for the session and am getting the same behaviour.
I've taken my test a stage further- I created a basic form and submitted to the url and the method worked as expected. My current form data is submitted by jquery ajax and assumed they were fairly identical in behviour.
My jquery submit function is this:
$.ajax({
url: '/products/addToCart',
type: 'POST',
async: false,
})
.done(function() {
console.log("success");
})
.fail(function() {
console.log("error");
})
.always(function() {
console.log("complete");
});
return false;
I set async to false - I assume to await the server response. (doesnt work if true either).
So the problem is a subtle difference between a form submit and an ajax submit. Both methods are hitting the same route and method - one saves the session data - the other one doesnt.
How can I overcome? Jquery submit is essential to this part of the app.
Success!
I found a similar problem relating to laravel 3. For the session to persist in an ajax call I need to return the response correctly.
return json_encode($response);
This is causing the problem. It's not it appears a valid response to enable the session to persist. I changed it to:
return Response::json($response);
This enabled the session to persist!
For some reason a normal form submit or call to the method allows the first one but ajax does not.
I've seen references elsewhere about echo statements in the method affecting the session data but did not think I had any - the return I suppose must behaving similar to an echo
Happy now (till the next problem)
This is the post that triggered the solution:
http://forumsarchive.laravel.io/viewtopic.php?id=1304
i have same issues, but when returning XML Response, not JSON.
I fixed it using session save.
\Session::put('LOGADO.ID_LOJA', $id_loja.time());
\Session::save();
This fixed everything inside AJAX Calls.
This is in reference to the solution that Ray gave.
I had a very similar problem, which your solution resolved. Initially, I had the following on my Controller:
echo json_encode(array('auth' => $auth));
I changed this to:
return Response::json(array('auth' => $auth));
However, this was only part of the solution. In my javascript file, I initially had:
data = $.parseJSON(data);
Which, if I had kept the echo in the controller...would have been needed. apparently Laravel will do some magic behind the scenes when using Response::json() so that the data that is returned is already parsed. Removing that line in my javascript file made everything happy again :)

Laravel 4 AUTH via AJAX, possible session issue [duplicate]

I'm having ongoing problems with laravel 4.1 sessions and getting unexpected behaviour.
Depending on how I call the controllers method the session either works or doesn't My app makes a call to a POST route - to add items to a cart which is a session. For some reason the session does not get updated.However if I make a call to the same function with a GET request the function works as expected.
My routes.php contains these two routes:
Route::get('testAdd', array('uses' => 'ProductsController#addToCart'));
Route::post('products/addToCart', array('uses' => 'ProductsController#addToCart'));
Both point to the same method
The method is currently this (for testing):
public function addToCart() {
Session::put("addcarttest", "add to cart");
return json_encode(Session::get('addcarttest'));
}
If I call the function with the POST method (with form data) I get the expected result and the contents of the session.
However If I then check for the session (using a profiler) it does not exist. The data did not persist.
If I then call the same method using the GET route, I get the expected result but importantly the session persists.
I thought maybe the POST method deleted sessions however once it exists it stays there - if I use the GET method and the sessin exists if I then try the POST method example again the session remains in place - so the POST method doesnt delete the session.
This is driving me crazy - I've lost a lot of hours over this and can't see why.
Am I missing something over how Laravel handles POST v GET ? Why would two different methods make a difference to underlying functions?
What do I need to do to make the session work correctly with POST?
Update:
I've now tried database driver for the session and am getting the same behaviour.
I've taken my test a stage further- I created a basic form and submitted to the url and the method worked as expected. My current form data is submitted by jquery ajax and assumed they were fairly identical in behviour.
My jquery submit function is this:
$.ajax({
url: '/products/addToCart',
type: 'POST',
async: false,
})
.done(function() {
console.log("success");
})
.fail(function() {
console.log("error");
})
.always(function() {
console.log("complete");
});
return false;
I set async to false - I assume to await the server response. (doesnt work if true either).
So the problem is a subtle difference between a form submit and an ajax submit. Both methods are hitting the same route and method - one saves the session data - the other one doesnt.
How can I overcome? Jquery submit is essential to this part of the app.
Success!
I found a similar problem relating to laravel 3. For the session to persist in an ajax call I need to return the response correctly.
return json_encode($response);
This is causing the problem. It's not it appears a valid response to enable the session to persist. I changed it to:
return Response::json($response);
This enabled the session to persist!
For some reason a normal form submit or call to the method allows the first one but ajax does not.
I've seen references elsewhere about echo statements in the method affecting the session data but did not think I had any - the return I suppose must behaving similar to an echo
Happy now (till the next problem)
This is the post that triggered the solution:
http://forumsarchive.laravel.io/viewtopic.php?id=1304
i have same issues, but when returning XML Response, not JSON.
I fixed it using session save.
\Session::put('LOGADO.ID_LOJA', $id_loja.time());
\Session::save();
This fixed everything inside AJAX Calls.
This is in reference to the solution that Ray gave.
I had a very similar problem, which your solution resolved. Initially, I had the following on my Controller:
echo json_encode(array('auth' => $auth));
I changed this to:
return Response::json(array('auth' => $auth));
However, this was only part of the solution. In my javascript file, I initially had:
data = $.parseJSON(data);
Which, if I had kept the echo in the controller...would have been needed. apparently Laravel will do some magic behind the scenes when using Response::json() so that the data that is returned is already parsed. Removing that line in my javascript file made everything happy again :)

laravel 4: why is Request::header() not getting the specified header?

I'm trying to get a header value with:
Request::header('csrf_token')
though, my firebug says in the headers that I have the csrf_token set to baMDpF0yrfRerkdihFack1Sa9cchUk8qBzm0hK0C. In fact, I can get that csrf_token instead with a native php code:
getallheaders()['csrf_token']
Now the question is am I doing my XSRF-protection right? or maybe there is a flaw in that php code I did, that I really have to use buggy laravel 4 function
Request::header('csrf_token')
which returns nothing but blank. And I just missed something. maybe in my Laravel 4 configurations, etc?
P.S: I am using AngularJS, but maybe it does not matter what clientside I use. I have this link as my guide: How to send csrf_token() inside AngularJS form using Laravel API?
I solved the problem by removing the underscore '_' in csrf_token so it would be crsftoken instead.
Request::header('csrf_token'); // Not working
Request::header('csrftoken'); // Working!
I think the problem there is that in the following answer at How to send csrf_token() inside AngularJS form using Laravel that you used, the csrf_token is not sent in the header of your XMLHttpRequest but in the form it self.
You need then to filter it in your laravel backend as a regular Input field. See below for a working example :
Route::filter('csrf_json', function()
{
if (Session::token() != Input::get('csrf_token'))
{
throw new Illuminate\Session\TokenMismatchException;
}
});
UPDATE
If you want to use headers in Angular, you would rather write something like :
$httpProvider.defaults.headers.common['Authorization'] = TOKEN;
In order to aplly a new header to your XMLHttpRequests. Then it is easily catchable even with raw php such as :
$aHeaders = getallheaders();
if (Session::token() != $aHeaders['authorization']) etc.
Problem
Laravel is removing headers with an underscore in the name when retrieving them with the Request::header() method. Additionally, all header names are converted to lower case in the Request::header() method.
Short Solution
On the frontend, replace all underscores in header names with dashes. csrf_token becomes csrf-token
Long Solution
Add the Laravel CSRF token as an Angular constant on your main page / layout.
<script>
angular.module("myApp").constant("CSRF_TOKEN", "<?php echo csrf_token(); ?>");
</script>
Add the token as a default header for all your requests in Angular.
angular.module("myApp").run(function($http, CSRF_TOKEN){
$http.defaults.headers.common["csrf-token"] = CSRF_TOKEN;
})
Have your csrf filter in Laravel check for a match in the headers rather than an input.
/**
* Check that our session token matches the CSRF request header token.
*
* #return json
*/
Route::filter("csrf", function() {
if (Session::token() !== Request::header("csrf-token")) {
return Response::json(array(
"error" => array(
"code" => "403",
"message" => "Ah ah ah, you didn't say the magic word.",
),
));
}
}
Request::header() is indeed used for the retrieval of headers, but check where the token is being set.. the CSRF token should be placed into the session by Laravel, and then it can be accessed through the Session::token() method.
If you look at the HTML generated through calls to the Form:: class, you'll see a hidden element called _token, which should then be compared to the token in the session. You can access that using Input::get('_token'), as with any other incoming GET or POST variable.
...However, all this shouldn't really be necessary, as it can be managed easily through the pre-defined CSRF filter in filters.php, just add that filter to the desired route or route group and you'll be protected, without having to get into the details of it.
the problem is with the Symfony Request object, which is extended in the Laravel framework. See this github thread
https://github.com/laravel/framework/issues/1655#issuecomment-20595277
The solution in your case would be to set the header name to HTTP_CSRF_TOKEN or HTTP_X_CSRF_TOKEN if you like prefixing X to your custom http headers.

Jquery Validate: How do I call the PHP file

I am very new to this javascipt, jquery world and I am having great difficulty getting to grips with it.
I have got the following in my HTML
$(document).ready(function(){
$("#frmEnquiry").validate();
});
</script>
It works although I do not understand, what is going on behind that statement, especially as it validates on each field entry (which is wonderful).
I have action="" in my form definition and a submit button and I have created a process.php file which further validates and sends an email, but I have no idea how I call that php file or where I put the call or what the syntax is for doing it. I am assuming it is based on a return from validate().
Also what is the correct procedure for returning from the PHP file with either success or failure messages.
If anyone can help at a simple syntax level, I would be most grateful as I have looked at so many possible solutions, my head is just totally confused.
I apologise for probably asking a very common question, but when you are just starting, it is difficult finding an example which you fully understand.
Well, there are two general approaches:
Oldschool form processing: you set <form action='process.php'> so your request is submitted to that page. Browser reloads and displays the content that you have rendered in process.php
Ajax-submit: this way you leave action blank, block submitting with something like <form onSubmit = 'return false'> and then send content using one of jQuery's AJAX-methods: get, post or ajax. It looks something like that:
$(document).ready(function(){
if($('form').validate()){
$('form').submit(function{
//Personally I prefer ajax as get and post are just shorthands for this method
$.ajax({
url:'process.php',
dataType:'json',
data:{name:nameVal, value:valueVal}
success: function(){
/*server returned some data, process it as you wish*/
}
});
});
}
});
Don't forget the principles:
PHP is executed server-side
javascript is executed client-side
Thus, PHP will only be executed if you are calling the server, meaningly, if you call an URL (http://mydomain.com/MyPHPpage.php) or if you are calling your server through an AJAX request (which is seemingly the same, except there is no reload client-side).
On the contrary, javascript is called only client-side. So the code used to validate() your form is called only on the client-side (except if you have a specific function behind this that would call the server). Hence, the javascript will use the parameters you gave him to validate the form without calling the server. This is a very good way not to overload your server with un-wanted request (empty fields, wrong e-mail addresses...).
Anyway, you can still have some other checks on your server side (checking in your database if the user exists...) and return another message to the user.
To answer your question, i'll ask some other ones: what are you trying to validate? do you want to check client-side or server-side? which data and for which purpose?
You need an AJAX call to your PHP script. If your form data is valid you would do such a call. You can asign a success callback to the ajax request where you can process the return data from PHP and either update data, display a error message or something else. From PHP you can return an object or an array where you detail if errors occured and of what type. More about AJAX -> http://api.jquery.com/jQuery.ajax/
In your case it is reasonable to perform form validation via javascript by checking the value of each field.
There is no need to send data to the server and receive information about their correctness. This method complicates the validation and is not improve protection against bad data.

Cross Domain Ajax Request with JQuery/PHP

Help, if you can-
The situation:
http://foobar.com includes a remotely hosted javacript file (http://boobar.com/stuff.js).
The goal is to just get an alert from the remotely hosted php script on foobar.com
I have tried the following code in stuff.js:
$.ajax({
type: "GET",
url: "http://www.boobar.com/script.php?callback=?",
dataType: 'jsonp',
success: function(result) { alert(result); }
});
No luck.
$.getJSON("http://www.boobar.com/script.php?jsonp=?",
function(data) { alert(data); }
);
Also no luck.
On the php side I have tried both the following:
return json_encode(array(0 => 'test'));
echo json_encode(array(0 => 'test'));
In Firefox I get a security error. I understand that it thinks I'm violating the security model. However, according to the jquery documentation, I should be able to accomplish this.
The error seems to be a security feature of the Same Origin Policy: to simplify, you can only make AJAX requests for stuff on the originating server (http://foobar.com). One way around this is to make a simple facade on the originating server, e.g.:
<?php
// this file resides at http://foobar.com/getstuff.php
echo file_get_contents('http://www.boobar.com/script.php?callback=?'
. $possibly_some_other_GET_parameters );
?>
Then, from foobar.com, you can make an AJAX request for http://foobar.com/getstuff.php (which in turn makes a HTTP GET request from your web server to boobar.com and sends it back to the browser).
To the browser, the request goes to the origin server, and is allowed (the browser has no way of knowing that the response comes from somewhere else behind the scene).
Caveats:
the PHP config at foobar.com must have allow_url_fopen set to "1". Although this is the default setting, some servers have it disabled.
the request to www.boobar.com is made from foobar.com server, not from the browser. That means no cookies or user authentication data are sent to www.boobar.com, just whatever you put into the request URL ("$possibly_some_other_GET_parameters").
You can get data from another server asynchronously using script tags and json:
<script type="text/javascript" src="http://somesite.com/path/to/page/"></script>
You can use this to dynamically load a remote javascript (by created a new script element and setting the src attribute, then loading into the DOM), which could set a variable. However, you need to really trust the remote site, because the JS will be evaluated without any precondition.
There is a method called window.name transport or window.name method which uses a general browser bug(not sure if this is a bug actually). You make the request through an iFrame and the loaded page puts the information you need to the "name" property of the JavaScript window object of itself.
This method uses a "blank.htm" since it first navigates to the target page and then goes back to the blank.htm page to overcome the "same origin policy" restriction.
Dojo have implemented this and you can find a more detailed explanation here.
Also I have implemented a cross-domain XMLHttpRequest object based on this method in the library I have written which can be found here.
You may not be able to use the library since it will need 1 or 2 additional libraries which can be found here.
If you need further help in implementing it in your style, I'll try to do my best.
So what I ended up doing, since it was just a GET - no data need to be retrieved - I used JQuery to create a hidden iframe with the URL including the variables I wanted to pass set as the source. Worked like a charm. To all who provded feedback - Thanks!
How about this !! Using a php proxy.
Cross-Domain AJAX calls using PHP
http://www.phpfour.com/blog/2008/03/cross-domain-ajax-using-php/
jQuery .ajax also has a setting 'crossDomain'.
http://api.jquery.com/jQuery.ajax/
crossDomain (default: false for same-domain requests, true for cross-domain requests)
Type: Boolean
If you wish to force a crossDomain request (such as JSONP) on the same domain, set the value of crossDomain to true. This allows, for example, server-side redirection to another domain. (version added: 1.5)

Categories