Home  >  

jQuery/Server Side Tip on Detecting Ajax Calls

Author photo
AddThis Social Bookmark Button
Here's a question I get asked from time to time. Is there a way to know when a request is being made by Ajax? For example, maybe you've built some cool Web 2.0 site and you don't want people accessing resources directly in their browsers. Perhaps you have a simple "user name available" service that is called by some front end JavaScript code. A hacker could use Firebug to discover the URL being hit and open it up in a new tab. They could then write an automated script to discover existing usernames. Given that concern, is there anyway to detect requests made via Ajax so that you can block non-Ajax calls?

No.

Let me be clear - there is no way to 100% accurately determine if a call was made via Ajax versus a "normal" request. Why? Because Ajax requests are normal requests. To the browser, it is just one more GET or POST request. Whether it was done by you entering the address in the browser or by JavaScript code, it's all the same.

That being said - if you are willing to accept that you can't perfectly detect Ajax requests, you can use a handy feature in jQuery to at least try to make the determination. I didn't know this until this week (I was preparing for a presentation on jQuery), but apparently jQuery will automatically add a header to all Ajax requests. Let's look at some sample code first and then I'll show you what I mean.

First, our sample HTML page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untitled Document</title>
<script src="/jquery/jquery.js"></script>
<script>
$(document).ready(function() {
 	$("#test").load("header.cfm")	   
});
</script>
<style>
#test {
 	border: medium solid red;
 	padding: 10px;
 	background-color:#FFC
}
</style>
</head>

<body>

<div id="test"></div>
</body>
</html>

There isn't much going on here. I've set up a simple div (with a lovely border and background) and told jQuery that as soon as the page loads, it should make a simple GET request to header.cfm. Let's look at header.cfm:

<cfdump var="#getHTTPRequestData()#">

Yeah, not a lot there. Even if you don't know ColdFusion, you can probably guess that this will display all the HTTP request information sent to the page. Opening up test.html in the browser shows this in the dump:

Picture 1.png

It may not be obvious, but the last header, X-Requested-With, with the value "XMLHttpRequest", was injected by jQuery. If we run test.cfm directly in our browser, the header is missing:

Picture 2.png

Nothing special was done in my code to add this header, jQuery just does it automatically. Again, this is not perfect, but it will probably stop the typical lazy script kiddy for going further. To actively block the non-Ajax request would be up to your server-side language of choice. Here is how I did it in ColdFusion:

<cfset reqData = getHTTPRequestData()>
<cfif structKeyExists(reqData.headers,"X-Requested-With") and reqData.headers["X-Requested-With"] eq "XMLHttpRequest">
	Woot! An Ajax request!
<cfelse>
	<!--- do nothing, log an attempt, throw an error, anything... --->
</cfif>

That's it. Now when I try to run header.cfm directly I see nothing, but when I run test.html, the ColdFusion code will output my message. Anyone out there actually doing something like this?

Read more from Raymond Camden. Raymond Camden's Atom feed cfjedimaster on Twitter

Comments

12 Comments

Gary Fenton said:

This is a good topic for discussion as I've often tried different things to stop people messing and hacking with my server side responses to AJAX requests.

What I did was check that every request had a referrer in the header and the referrer included the correct page on my server and it was from the right domain too. Of course the referrer can be spoofed so it's not foolproof.

Your suggestion to look for a jQuery header is as fragile as my method which is basically security by obscurity. Anyone determined to beat your database senseless with a barrage of requests or to try to extract data in an easily stealable and reusable format can do so with little effort.

A newer method I've conceived is to grant the browser a window of opportunity to send requests to your AJAX backend script. Your main web page is given an encrypted token which must be sent back to the server with each AJAX request. The token is valid for a fixed period of time, say 5 minutes, after which the server rejects all requests that have an expired token - or it can return a 404 to really confuse a script kiddy.

For normal users the main web page must be refreshed to get a new token, which the web app can do automatically. For for a script kiddy they must spend time working out why their requests have stopped working for no apparent reason even though they are posting the original token back to the server. If they do figure it out they have to manually grab a new token every 5 minutes to continue their tomfoolery.

Ray, I'm sure you can grow this idea to make it even tougher.

Travis Almand said:

@Gary - that's an intriguing idea. You should write that up somewhere if you could be compelled to share.

Raymond Camden said:

I'll ditto Travis. Gary, if you were to blog this, let me know and I'd be sure to post a link from here.

I'll point out that you could also do the token/check completely server side. Ie, when the browser makes the initial request, set a session variable that notices the time. When the Ajax-based requests come, compare current time to the session variable. That hides a bit of the logic from the front end and could possibly make things a bit tighter.

Thomas Case said:

Ray,
Good to see your post here. You always have a way of explaining things more plainly, more fully, and with less words. It is good for the rest of the programming world, outside of the CF community, to experience your talent.

Regarding your topic, if users want to restrict access to their AJAX server side service, then they need to set up authentication mechanisms. Anonymous requests are "anonymous." You don't know where (really) they originated from, if coming from the Internet.

Gary Fenton said:

Okay, I'll come up with some code or a demo, but I don't have a blog. Maybe this would encourage me to have one and that could be my first post. :-) I'll reply later when I have something useful.

Incidentally, the captcha text here is REALLY difficult to read. 3rd attempt now...

Gary Fenton said:

I've written a demo app to show how my idea works. It includes a little interface so you can try hacking the params posted to the server.

Trouble is I don't have a suitable ColdFusion server on the Internet that I can upload it to. Can someone host a few files for me please so others can try out the demo? Ray, buddy? :-)

Raymond Camden said:

Ping me via email. Ray at camdenfamily dot com.

Gary Fenton said:

Ray, many thanks for hosting the demo scripts. The live demo is here: http://www.coldfusionjedi.com/demos/ajaxBouncer/

I also blogged about it here: http://garysgambit.blogspot.com/2009/04/ajaxbouncer-limiting-ajax-tampering-and.html

Cheers Ray!

Gary Fenton said:

Ray has kindly hosted the demo app here for everyone to try out.

You can try tampering with the data in the grey area. Read the instructions first!

I've blogged about the concept here.


The demo code can be downloaded here

ZK@Web Marketing Blog said:

1. Ajax -
Example Jom Comment or The youtube comment system..
move the wysiwyg editor in the comment page not in a new page

2. Comment Pagination
make an option so that user can select how many comment per page

3. Report User
example like in CBB

4. ignore user
user can choose to hide/block a particular user's comment from their view

5. notifications
example cbb


if you have other suggestion..please add

John Richards said:

Hi nice article Raymond.

You might also want to check out StreamHub Ajax Server.

Thanks,
John.

Alan Sumali said:

Your blog is so informative … ..I just bookmarked you....keep up the good work!!!!
SaleHoo 2

Leave a comment


Tag Cloud

iPad

What's your take on the iPad? (Putting aside the Flash/iPad flame war)

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.