Secure your Ajax requests with jQuery
Ajax requests suffer from the same Cross Site Request Forgery attack vectors as normal pages. Many developers assume that a given ajax request will only take place on their site, and therefor skip out on the security. Not true.
Google found out the hard way when security researcher Jeremiah Grossman uncovered a flaw in the way Gmail implemented its contact list ajax requests. When the browser loaded Gmail, it would make a request to Gmail and load a users contacts, among other things. Gmail returned the contacts as an array of JavaScript objects.
Now normally you wouldn’t be able to exploit this. Browsers are subject to the cross-domain policy which prevents it from performing Ajax requests to another domain. Grossman was clever enough to rewrite the Array object\function and then load the Contacts URL through a normal <script> tag. Since the <script> tag allows you to load content from any domain, the browser successfully downloaded the file and executed it. As it processed the array, it executed the Array object\functions that were rewritten above. This allowed the browser to successfully read the contacts array cross-domain. Genius. While this vulnerability was quickly fixed by Google, it proved how easy it is to still grab data cross-domain. We would need some more security built in. Read more about this hack here.
So as I was working on a project of mine, I started to think about this type of attack. I am using jQuery for my Ajax but I noticed that jQuery does not have a built in shortcut to do an Ajax POST request. There exists $.getJSON() but no $.postJSON() – so I made one, and its quite simple.
// $.postJSON()
$.postJSON = function(url, params, callback) {
$.post(url, params, callback, "json");
};
But this wasn’t safe – there was no security built into this method. How could I secure my JSON payloads that came from the server. I began to analyze some other sites and the results are interesting. I discovered 3 ways of ensuring that my data couldn’t be loaded using a <script> tag like in the case of the Gmail attack.
All three security measures involve prepending the real JavaScript with something that doesn’t belong there.
| Site | Prepended Content | Result |
|---|---|---|
| Gmail | while(1); | Causes an infinite while loop |
| for(;;); | Causes an infinite for loop | |
| dont be evil; | Invalidates the JS because it is plain text |
Each of the above methods will prevent your code from executing should they be loaded in to a page using the <script> tag. For this to work, your application would have to supply the content with one of those three values already prepended to your content.
I choose to use the Gmail method of placing while(1); before my JSON responses. If I try to load that request with a <script> tag, the browser locks up completely. This is totally acceptable as we do not want anyone doing that in the first place.
I rewrote the $.post() method to take this into account when processing JSON POST Ajax requests. The new code is as follows:
// secure $.postJSON()
$.postJSONsecure = function(url, params, callback) {
$.post(url, params, function(data) {
var secureData = eval('(' + data.split('while(1);')[1] + ')');
if (typeof callback == 'function') callback(secureData);
});
};
It does the same thing as the $.postJSON() does, except that it looks for “while(1);” and only returns the data that exists to the right of that string.
Thanks for this very simple yet genius idea.
I’ve been worried about xss attacks myself and had almost given up hope of finding a solution.
I actually found this article while searching for a way to encrypt a login request to guard against sniffing. Glad I stumbled here and found an answer to a question I thought unanswerable.
Cheers Bryan! You’re a star