Assuming Secure Forms is enabled:
In all previous versions of ExpressionEngine doing a POST request to an action URL - eg. http://domain.com/index.php?ACT=XX - did not require a XID field in the POST.
Doing the same now in EE 2.7 returns an error: The action you have requested is invalid.
When i add the XID field the AJAX request works fine. (using window.EE.XID) However, adding the XID field will invalidate the XID and sending a subsequent AJAX request fails again.
If you where working on a form this will also make the form useless, since submitting the form will redirect you to the CP homepage.
Are we supposed to refresh the XID every time we do an AJAX POST request? Kind of cumbersome.
:(
This is a high priority as all of our addons that use ajax are broken by this!
Brad
Thanks guys!
Am I correct in my interpretation that you are using action requests on the backend? I believe that is why it trips up since they’re not considered to be CP requests.
The long term solution here is most definitely an XID refresh when the ajax request returns. I think we can make that mostly transparent on the CP side of things. The frontend is a different story, that may need to be at least semi-manual (some sort of trigger to include the refresh code).
Hello,
We are indeed sending AJAX requests to the ACT URL from the CP. This worked in EE 2.6 and below perfectly fine with no XID.
Refreshing a XID after each AJAX request is very frustrating. Imagine uploading 10 images with Channel Images, after each image upload I would have to fire another ajax request to refresh the XID.
Security is fine and all but it shouldn’t wreck usability. I believe there should be another way to do AJAX requests from the CP.
Note: We also do AJAX request from the front-end (safecracker) for modules.
Regards,
Brad
Security is fine and all but it shouldn’t wreck usability.
Correct it shouldn’t wreck usability, but security against CSRF is not something you should dismiss so easily just because AJAX posting worked without it. CSRF is extremely dangerous. As an example, let’s say that you are a logged in super admin at expressionengineconference.com. Without CSRF protection, If I can get you to visit my site, I can add some JS to my page that will make your browser submit data to your site that would be received with all of your full super admin permissions. Posting new data, deleting members, deleting content, executing queries, etc.
Derek,
I do understand the need for CSRF protection, I am obviously not against it. But having to do 2 ajax request for 1 is a bit over the top right? I am sure there has to be a better way to do this. Companies like Facebook don’t use this system and I am pretty sure they have figured out a way to prevent CSRF.
So with your current solution I have to do this:
1) AJAX for new XID Code 2) AJAX to upload 1 image (invalidates the XID) 3) AJAX for new XID Code 4) AJAX to upload 1 images (invalidates the XID)
and on and on…
I am pretty sure this only works in CP since the method you use to refresh the XID (I think) only works in the CP. What will I do in SAEF? There I can only do ACT requests and they require a valid XID now.
I hope you understand how frustrating this is for CI, CF, Updater and so on. I am not against improvements and security, but I believe customers will not appreciate the step back in efficiency and speed.
Respectfully,
Brad 😉
After looking around a bit, it turns out that there is a theoretical csrf attack on ajax requests as outlined here.
So with the same-origin security model out of the question, I’ve gone with a variant of their solution. All ajax requests, regardless of CP or not, will now be expected to provide an X-EEXID header[1]. They will be returned a fresh XID in the same header of the response. I decided to make it header based so we don’t have to worry about exit()’s and injecting objects where strings are sent back.
On the cp side of things this will happen completely transparently as long as you use jQuery’s ajax interface.
Our javascript is essentially doing this:
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
var old_xid = EE.XID;
jqXHR.setRequestHeader("X-EEXID", old_xid);
jqXHR.complete(function(xhr) {
EE.XID = xhr.getResponseHeader('X-EEXID');
$('input[name="XID"]').val(function(i, current) {
return (current == old_xid) ? EE.XID : current;
});
});
});Open to ideas for how to do this on the frontend. It would be easy to add the above snippet to the jquery module output, but not everyone uses that (myself included).
[1] If no header is present, we fall back to POST for backwards-compatibility.
Hey guys,
Running into this issue as well with Low Search. The Low Search form allows you to override the global Secure Forms setting, by not generating an XID. Pre-2.7 that worked just fine. No XID posted, no check.
But now, I’m getting the invalid_action message. This is problematic, because lots of times people would perform the search, hit the back button, and search again. That would cause the ‘not authorized’ error message, because the previous XID was already validated and removed. So I built in the secure=”no” setting to make sure no XID is submitted at all regardless of general preferences, avoiding the error altogether. This also is the case if people send the form via Ajax on their front end.
I don’t think there is a csrf security issue with just submitting a search form, right? I’m sure in some cases it’s good to have the secure forms option enabled, but in this case, it’s more of a nuisance then a help, I reckon.
Thoughts?
Greg,
It’s not changing from the perspective that we require an XID for POST requests.
On the backend you shouldn’t have to do anything in the next preview. Do you have a lot of frontend ajax requests? Can we give you a method that injects that bit of jquery in the page? I certainly don’t want every dev to have to copy that bit of JS. On the other hand, I don’t want to just globally inject it on the frontend if it’s not relevant to that request.
A nice middle ground might be to inject it if form_declaration is called.
We have a few addons that use ACT ajax for Safecracker, Tag for instance that POSTs to ACT for autocomplete, plus things like Freeform, Super Search, and User, etc that users very often build front-end ajax things with. Since those are outside safecracker and the JS EE object can’t be count on to be present, I will need to send the XID with responses and have the end-users change the XID out on their form.
In those situations, its going to break upgrades for people until they change their templates. We cannot assume that they are always using jQuery, etc, so we are going to need to add instructions for what users are going to need to change to get their templates working again, in addition to us needing to send new XIDs for them.
We are currently doing the XID handling manually on the front end for things like Freeform, User, etc, for when a end-user submits a form and gets errors and wants to use the back button. We just don’t delete the XID until they get past errors. Looking at the code, if I am correct, automatic XID checks on POST are now being run inside of Core->ee_run() before any modules are being called. So we will need to add restore_xid() to those where appropraite because the XID will now be self deleting, where before we were not manually deleting it until a successful POST.
Packet Tide owns and develops ExpressionEngine. © Packet Tide, All Rights Reserved.