GroceryCRUD add, edit buttons not working when enabling CodeIgniter CSRF protection - codeigniter

I am using GroceryCRUD 1.5.0 with CodeIgniter 2.2.0.
When enabling CodeIgniter's internal CSRF protection with:
$config['csrf_protection'] = TRUE;
in application/config/config.php, then the GroceryCRUD auto-generated action buttons (edit, view) and links (add) does not work anymore.
It seems that the CSRF token is not passed along in the Ajax calls (confirmed with Firebug). It is possible to use this CodeIgniter feature with GroceryCRUD?

I finally managed to solve my problem. Two options are available:
The easy way:
Set:
$config['grocery_crud_dialog_forms'] = false;
in application/config/grocery_crud.php.
This option works well without CSRF protection enabled (that is, it can be set to true to produce more elegant forms), but fails when set if no code modifications are done in the javascript.
The elegant way:
If we want to use:
$config['grocery_crud_dialog_forms'] = true;
in application/config/grocery_crud.php to have the cute forms, then:
include the jquery.cookie plugin in pages with forms
add this code to your JS files to auto-magically insert the CSRF token in all ajax POST calls:
$(document).ready(function() {
var csrf_token= $.cookie('csrf_cookie_name');
$.ajaxSetup({
data: {
'csrf_test_name' : csrf_token
}
});
});
I hope this will help someone else.

Just in case someone has the same error: For CI 3.0.1 and GroceryCRUD 1.5.1, Cookies are properly sent with AJAX requests, however because the token changes, only the first request will work.
To always use the same token, set (in application/config/config.php):
$config['csrf_regenerate'] = FALSE;
Edit: Manual for reference: http://www.codeigniter.com/user_guide/libraries/security.html#cross-site-request-forgery-csrf

Related

Creating database entries in Exist-DB with Ajax calls

While developing a self-contained exist-db app I ran into some issues with passing on the user's identity during an ajax call.
I have created a standard page that allows the user to login in with his or her standard exist-db account. (It's more or less based on this https://github.com/eXist-db/existdb-login ).
At some point, the users need to create their own entries and upload files, and this is done with an ajax script (I'm using Kartik's bootstrap uploader, but it's basically a standard ajax upload function):
$('#input-24').fileinput({
uploadUrl: "../modules/uploader.xql",
uploadAsync: true,
fileActionSettings : {
uploadExtraData() {
return {
'FileName': $(this).data('title'),
}
}
},
});
Now, of course, this will fail, since it will appear like a GUEST user is trying to create entries, which is an action that is allowed only for registered users.
Does exist-db allow a way to send in my login credentials at this point without having to resort to the standard HTTP login (which is problematic, since it means creating a cookie to memorize the password, which more or less renders the whole login suing exist's mechanisms useless), or is there any way to use the controller instead of an external script?
Thanks in advance!
I think you can add the user and password into the URI to trigger the Ajax client to do basic with. e.g. http://username:password#your-server/some/uri/modules/uploader.xql
For simple, basic authentication we do the following since the page itself is being served from xQuery.
step 1: we create in the page xQuery a variable containing the session info. This should be fine for you ... the result of your login would be an HTML page:
let $sessinfo := util:base64-encode(concat(request:get-parameter('user', ()), ":", request:get-parameter('pass', ())))
step 2: during the build of the result page in xQuery, we turn that into a javascript variable through a simple script command placed in <head> tag:
<script>
var sessinfo = "{$sessinfo}";
</script>
step 3: in Javascript loaded with the page (we use jQuery), after the page is ready we setup authentication for all AJAX requests:
jQuery.ajaxSetup({
headers: {
"Authorization": "Basic " + sessinfo
}
});
After that, any AJAX request made would send basic authentication header with user and password to other xQueries executed with jQuery.ajax
To have an eXistdb session work with XHR (Ajax requests) you need make sure that
login:set-user('my.login.domain', (), false())
is called in the controller before you forward it to your request handler. Otherwise all request will seem to originate from the 'guest' user.
If you want to use vanilla/native JavaScript requests e.g. fetch you also need to tell it to add the credentials to the request:
fetch(url, { credentials: 'same-origin', method: 'GET' })
By the way, the persistent login used in exidtb-login likely uses a cookie value to pick up the session variables stored on the server (JSESSIONID), but I need to check that.

Template-less Django + AJAX: Does Django's CSRF token get updated during the course of a browsing session?

My current setup is AngularJS + Django 1.5 and I have completely thrown away the use of Django's template engine (ie. the backend is pretty much an API server).
Since I am not using the csrf_token template tag, Django, in turn, does not set and send the csrftoken cookie in response. As instructed by the official docs, the ensure_csrf_cookie() decorator should be used to force the decorated view to send the csrftoken cookie.
I have applied the ensure_csrf_cookie() decorator to the view, which serves the first GET request that my web client calls at bootstrapping. With that, my web client gets a hold of the CSRF token and henceforth is allowed to call unsafe methods (ex. POST) to the server.
The above setup works fine only if the CSRF token remains the same until the browsing session ends.
Question: Does Django's CSRF token get updated during the course of a browsing session? If 'yes', does that mean I would need to apply the ensure_csrf_cookie() decorator to all the views I have?
1) Does Django's CSRF token get updated during the course of a browsing session?
Looks like the CSRF token is unique per session, but it is based in my observations, I have no "official" source. With Angular.js I use the following code without problems:
angular.module('app', ...)
.config(function($httpProvider) {
var cookies = document.cookie.split(';');
var csrftoken = _.find(cookies, function(v) {
return v.trim().indexOf('csrftoken=') == 0;
});
if(csrftoken) {
$httpProvider.defaults.headers.common['X-CSRFToken'] = csrftoken.split('=')[1];
}
})
Since I serve the HTML from Django, by the time Angular bootstraps the cookie is already there.
2) If 'yes', does that mean I would need to apply the ensure_csrf_cookie() decorator to all the views I have?
You can try CORS instead if CSRF. Otto Yiu maintains the django-cors-headers package, which is known to work correctly with REST framework APIs.
Some (untested) ideas to apply ensure_csrf_cookie():
monkey-patch APIView
create a CSRFCookie mixin and add it to your views
apply ensure_csrf_cookie() to your base classes
Giving support to the #Paulo Scardine ideas of applying the ensure_csrf_cookie() (which I consider valid, and useful), I would like to add a new one possible solution to it, if you definitely have to ensure_csrf_cookie() in all your views. You could write a custom middleware, and implement the logic that is there inside the ensure_csrf_cookie. Something like this:
On your app.middleware.py:
from django.middleware.csrf import get_token
class EnsureCsrfCookie(object):
def process_request(self, request):
# Forces process_response to send the cookie
get_token(request)
and of courses on your settings file add the middleware to the MIDDLEWARE_CLASSES:
MIDDLEWARE_CLASSES = (
.,
.,
.,
'app.middleware.EnsureCsrfCookie',
.,
.,
.,
)
It is just one idea more to face this problem. I hope it can be useful for somebody in the future.

Setting AJAX headers in Breeze

How do I set headers before letting breeze make a request?
Example: my service expects a certain key to be part of the request in
a header name 'x-service-key'. Till now, I was using jquery ajax and
amplify, so pretty easy to set up the header. Since I don't have any
control over the request that breeze is making, how do I pass extra
stuff like headers?
This question was posted by sujesharukil on our IdeaBlade forums. I am reposting the question and answer here since I think it will be useful to the Breeze Stack Overflow community.
As of Breeze 0.70.1 we now support for the ability to completely customize or replace any Ajax communication between the breeze client and the web service on the server.
The Breeze documentation on our Ajax support is still in progress, but hopefully the following will get you started.
To control the headers on every Ajax request that Breeze makes, you can execute the following code when your app first starts up.
var ajaxImpl = breeze.config.getAdapterInstance("ajax");
ajaxImpl.defaultSettings = {
headers: {
// any CORS or other headers that you want to specify.
"X-Test-Header": "foo2"
},
};
Alternatively, you can intercept the individual Ajax calls and add your headers selectively based on the request.
var ajaxImpl = breeze.config.getAdapterInstance("ajax");
ajaxImpl.defaultSettings = {
beforeSend: function(jqXHR, settings) {
// examine the jqXHR or settings and customize the headers accordingly.
jqXHR.setRequestHeader("X-Test-Before-Send-Header", "foo2");
}
};

Extend/Override jquery.ajax.done function to detect .net logon screen

I'm using forms authentication on an asp.net MVC application and I'm using jquery and ajax to create a rich user interface. Some users tend to leave the page open for hours and their login session is expiring as expected. If the user click a link or reloads the page the login screen is returned as expected. If they click a button or trigger an ajax call the ajax call is returning the login html instead of the expected html or JASON result. Resulting in a mangled page with half a login screen.
As I've already coded a lot of ajax calls I thought I could extend or override the default .done event to add a check for the login screen before continuing with whatever event I've programmed.
For example i have this function :
function CallAjax() {
$.ajax({type: "GET", url: "foo" })
.done(function (data) { $('#result').val(data); });
}
is it possible to override the default implementation of .done so do a check without rewriting this function? My check would probably to see if the result is a html response if so search for "login" to see if its the login page.
I would recommend you to extend the ASP.NET Forms Authentication module as Phil Haack explained in this blog post so that it no longer redirects to the login page when requested with AJAX but returns a 401 HTTP status code instead. Then you could simply use the .fail callback and intercept the 401 status code. The .done handler will not be invoked in this case.
I tried Darin's suggestion but after hours of trying I just couldn't get it to work in my set up. However I stumbled across this rather simpler solution which worked for me first time:
just add this to the global.asax
protected void Application_EndRequest()
{
var context = new HttpContextWrapper(this.Context);
if (FormsAuthentication.IsEnabled &&
context.Response.StatusCode == 302 &&
context.Request.IsAjaxRequest())
{
context.Response.Clear();
context.Response.StatusCode = 401;
}
}
}
Taken from http://jvance.com/blog/2012/09/21/FixFormsAuthentication302.xhtml

Issue with METHOD in prototype / Ajax.Request

I am trying to call yahoo api via Ajax to find current weather:
var query = "select * from weather.forecast where location in ('UKXX0085','UKXX0061','CAXX0518','CHXX0049') and u='c'";
var url = 'http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent(query) +'&rnd=1344223&format=json&callback=jsonp1285353223470';
new Ajax.Request(url, {
method: 'get',
onComplete: function(transport) {
alert(transport.Status); // say 'null'
alert(transport.responseText); // say ''
}
});
I noticed, that instead of GET firebug says OPTIONS. What is it and how I can use force prototype to use GET?
Here is functionality which i am trying to recreate.
And here is full URL which I am trying to access:
http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20location%20in%20(%27UKXX0085%27%2C%27UKXX0061%27%2C%27CAXX0518%27%2C%27CHXX0049%27)%20and%20u%3D%27c%27&rnd=1344223&format=json&callback=jsonp1285353223470
After hours of trying to debug the same issue myself, I came to the following conclusion.
I believe this happens because of XSS counter-measures in newer browsers.
You can find very detailed information about these new counter-measures here:
https://developer.mozilla.org/en/http_access_control
Basically, a site can specify how "careful" the browser should be about allowing scripts from other domains. If your site, or a site from which you're loading external JavaScript code, includes one of these pieces of "browser advice", newer browsers will react by enforcing a stronger XSS policy.
For some reason, Prototype's Ajax.Request, under Firefox, seems to react by attempting to do an OPTIONS request, rather than a GET or POST, so perhaps Prototype has not been updated to correctly handle these new security conditions.
At least that was the conclusion in my case. Maybe this clue can help with your case...

Resources