A user sent in an interesting idea to me earlier in the week and I wanted to share it with my readers. It involves noticing and blocking Ajax-based requests. This kinda dovetails nicely with one of my recent InsideRIA posts (jQuery/Server Side Tip on Detecting Ajax Calls, and forgive me for not posting recent InsideRIA links - should I keep doing that?). Rob sent me the following:
So right off the bat, the first thing I mentioned was that the Method argument can be passed in via a form post as well, so he really needed to check url.method and form.method. I also mentioned that remote methods can be called via SOAP (web services). There's actually a CFML function for that: isSOAPRequest().I am getting into some AJAX-driven work using jQuery, and while it's all happening from secure areas of the site, I don't want people to try and backdoor into some query results by directly calling the CFC. I have 2 functions that are simple query select statements, but are accessed both publicly and remotely. What I came up with was to manage the CFRETURN calls so that if I detect the existence of url.method, to check the cgi.http_referer value to make sure it's from the pages that would be making the AJAX calls. If it is, I pass back the query. If it's not, I do a CFABORT (perhaps I'll log it in the future for what good that would do?). If I don't see url.method I assume it's not being remotely accessed so I just pass back the query.
All my queries are CFQUERYPARAM'ed so I'm not worried about injection attacks. But I was wondering if you think this is a strong enough gate to put up for this or if you have something more betterer. :)
The next thing I'd probably worry about is checking the referer. I've never actually looked at the referrer values in Ajax calls. I whipped up a quick test and the CFM I called via Ajax did report a referrer value of the page itself. I'd still not trust it 100%, but it could stop some people.
At the end of the day though I'm not sure it's worth it. Rob followed up with another idea I thought might make more sense:
I did just have a thought. I use onRequest to manage access to the admin sections and that is where the ajax calls come from. I have the code in onRequestStart that looks for .cfc extensions to allow the request to work so why not also add in there a check for a valid session?
I think this makes the most sense really. Well, technically, just ensuring the session variables exist. I'm not so sure it even makes sense anymore to bother checking for ajax versus non ajax calls. If a person is authenticated and authorized to run X, do we really need to care if they run it directly versus Ajax?
Again - open question here folks. Be sure to also read the comments over on the other post as well.
Archived Comments
imho relying on the referrer anytime is a potentially bad decision. Anyone that uses any kind of proxying to wipe it clean will run into problems using your shtuff. I've seen it several times now cause issues with someone's code trying to validate a form post :(
For all AJAX requests, I require a token. I send the AJAX requests to a proxy that evaluates the token, and relays the request to the appropriate CFC. The token is an encrypted string containing a time stamp, as well as a key to identify the requesting page and the user, if one exists. That way I can evaluate the legitimacy of the AJAX request, and if necessary, turn it down. I will turn down some requests due to stale time stamps, and some due to user permissions. You can develop any rules you like.
I load a new token with each page, and store it in a tag somewhere on the page usually as a class name, and then pass it along with any AJAX request using the id - $("#tokenContainer").attr("class")
I still think checking by SessionID is a safe way to go. On Ajax calls to CFCs or pages I want protected I pass the current SessionID as an argument. The SessionID on the server should match the one sent in the request so you know it isn't someone tampering with the call.
On Ajax calls that really need to be locked down I also check for the XmlHttpRequest header. It keeps an unscrupulous person from just opening another browser tab and trying to modify data by calling the CFC directly.
Add a request nonce. If the nonce gets out of sync or is missing, throw an error that has the UI redirect to a page that verifies their action.
Ray, I think that I agree that it doesn't matter if it is a remote call or not.
As for tokens or nonces, it seems to me that the sessionid should fill the requirements for either. If it is good enough for a regular HTTP request to verify the client, then why would an Ajax request require anything more? You can tamper with a regular HTTP request just like you could an Ajax request.
It seems to me that there is nothing that can be done from an Ajax request that can't be done via a standard HTTP request (which an Ajax request is), so why would anything extra need to be done to secure it.
I still like the idea of sensing if it is an Ajax request or not, because that might affect the format of my return data.
Ahem ... Demo Please?
Ray, after reading this post, I was truly inspired and I think made an awesome (personal) discovery that a remote method response is not tied in any way to the method invocation itself!
http://www.bennadel.com/blo...
Anyway, thanks for the inspiration!
Just call me the Inspirator.
Hi Ray
Is this a case where you could have used the verifyClient() attribute of the cffunction tag?
http://www.cfquickdocs.com/...
Cheers
Marty
Yep, that would work as well. Thanks for reminding me, Martin.
VerifyClient() does not seem to like to play well with jQuery. I think I am missing a piece of the puzzle, but the documentation does not mention much about it and there aren't many blog posts out the on it, other than it being one of the new Ajax features in CF8.
Is it possible to use it with jQuery?
VerifyClient ONLY works with "CFAjax", not other libraries.
Ah, the last piece of the puzzle. Explains all those pretty red 500 Server errors in ColdFire ;)
@Chris, I came up with the idea of encrypted tokens too. How spooky is that? See my blog for a working demo (kindly hosted by Ray)
http://garysgambit.blogspot...
I just found one place that will cause trouble with that X-Requested-With XMLHttpRequest header. If you use the jQuery Form plugin and it's ajaxSubmit() function (which I have found the best way to do file uploads so far), it does not contain that header. I am assuming it has to do with using the iFrame to manage the file uploads?
Either way, that threw a nice monkey wrench in what was otherwise a fairly decent setup I had going. Grumble.