Current Filter:
Date Range: 1/11/2011
(clear filters)

Tuesday, January 11, 2011

VerifyClient() with non-standard CF AJAX

If you've ever researched ColdFusion AJAX security features such as VerifyClient() or secureJSON or secureJSONPrefix, you've probably come across Ray Camden's article already (or one of his blog posts about it) but in case you haven't, before you continue I want you to at least read this post from Ray. (If I hadn't read Ray's postings I wouldn't have been able to come to the solution I'm going to tell you about without more effort. So thanks Ray!) Now, with that out of the way...

I'm using both some custom AJAX and some built-in ColdFusion AJAX goodness on a client's website. I only want AJAX calls to be able to get data from a particular page. Otherwise, I want to return a blank page to the user. In other words, I don't want someone browsing directly to callbyajax.cfm.

Now we all know that URL parameters can be hacked, but in my client's case if someone manages to pass in valid params that will get them results and they can bypass ColdFusion's VerifyClient() too, then that's good enough for me. With that in mind, my ideal solution would allow me to still use VerifyClient(), even though I'm using custom AJAX. So how can I make that happen?

Once you know that all VerifyClient() is doing is looking for a valid URL._cf_clientid, then it becomes a matter of passing in a valid _cf_clientid. The next thing that comes to mind is, "How can I get that value on my own so I can use it?" Unfortunately it wasn't as simple as I hoped... To my disappointment, the value used for _cf_clientid isn't anywhere to be found in the Client or Session scopes.

Something generates the parameter and the output looks something like this: ?_cf_clientid=C56E92048D21B1B348A3D097D842866B. I could only conclude that ColdFusion was using some sort of encrypted value and it didn't seem there was a way to access it natively. I was stuck. So I turned to my wonderful husband Dan who happens to be a ColdFusion genius, for some help and brainstorming. (Thanks for your patience Dan!)

Dan noticed that the URLs generated by native ColdFusion AJAX calls use some global JavaScript variables that ColdFusion sets right after the opening <head> tag. So Dan suggested that I use <cfajaximport tags="cfdiv" /> to force my page to write the global variables so that I could references them much like ColdFusion does. I didn't like that idea, mostly because importing tags I didn't need seemed wasteful. Once I got over trying to find a native way to get that same string of alphanumeric characters, I decided I should probably think about Dan's suggestion some more. I recalled recently seeing an example somewhere (though I can't recall where), that showed <cfajaximport /> without the tags attribute. Turns out, the tags attribute is optional, so I only had to use <cfajaximport /> in order to render the globals to the document. Ok, that feels a lot less dirty to me; I can live with this workaround.

Here's a simple proof of concept (by no means perfect) for you to view in the browser: VerifyClient.cfm. Notice the comment in the source code that points out that the globals were generated by using <cfajaximport /> at the top of the page. Here's the ColdFusion Code in the file that it links to (which is supposed to be called via AJAX, but faked for proof-of-concept purposes):

<cftry>
<cfset VerifyClient() />
<cfcatch>
NO!<cfabort>
</cfcatch>
</cftry>
YES!

The beauty of this solution is I can now use one file for both custom and built-in AJAX functionality and still use VerifyClient().

Don't forget: You must enable client or session management in your Application.cfm or Application.cfc file for VerifyClient() to work at its best. Without client or session management enabled, VerifyClient() will not throw an error if URL._cf_clientid exists. (Empty values and bad values will pass, but missing _cf_clientid entirely will throw the expected error.)

Sidenote: I ran a test to see if I could get away with passing _cf_clientid via the Form scope instead of the URL scope. No luck; ColdFusion is only checking for URL.

Posted by ~Angela | Comments (0) | Add Comment | Permalink