XMLHttpRequest - Security Bypass

While trying to help Dare make his MovieFinder page run in Firefox, I ran into an issue that can make developing AJAX applications a pain: when testing your pages, you need to host them in the same domain as your services.

I explain the details of the problem and how the "XMLHttpRequest - Bypass Security" Greasemonkey user script solves it.

Note: this script is meant for development only, as it gives the page access to a potentially dangerous API. The default @include is "file:///*", but feel free to restrict it even further to the path for the pages you're trying to tweak. You should never have to @include an http url.


Scenario:

Dare wrote a Virtual Earth hack, MovieFinder, that makes it easy to find movies in the Seattle area. Unfortunately, he only got it to work on IE and he requested help with fixing the javascript to make it work in Firefox as well.
The first thing I did was to copy index.htm and main.js locally, so I could tweak them. But when I tried to run it, Firefox wouldn't be able to make the calls back to Dare's server.
The error message I had was: "error: uncaught exception: Permission denied to call method XMLHttpRequest.open".
A security restriction within Firefox, the "same origin policy", causes this error. Essentially, javascript within a page should only interact with the same host that served that page.
This is good for security, as it closes a number of phishing and spying attacks, but it also makes tweaking XMLHttpRequest-based pages more difficult.


Solution:

Firefox does offer a way of expanding the privileges of a script, allowing it to make requests to different hosts. But it requires you to sign your scripts, which seems like a hassle.

Greasemonkey has its own XMLHttpRequest API, which is more powerful than that available to web pages. It bypasses the same origin policy, thus allowing user scripts to truly compose and remix websites.
XMLHttpRequest - Bypass Security replaces the regular XMLHttpRequest API from Firefox with an unsafe version based on Greasemonkey GM_xmlHttpRequest. It allows the local page that you are developing to make requests to an online service.


Usage:

When you install the user script and browse the web page you're hacking locally, XMLHttpRequest will continue to work.
The unsafe version of XMLHttpRequest that is injected supports the most common XMLHttpRequest APIs, although not all. The missing APIs are setRequestHeaders, responseHeaders, setting a username/password and data (for posts). Ping me if you really need them, I'll update the user script.


Notes:

This script doesn't work in combination with my previous development scripts (XMLHttpRequest Tracing and XMLHttpRequest Debugging). I haven't looked into it yet, as I didn't need that capability.

Although IE also follows a same-domain policy for javascript, the local web page opened in IE was able to connect to Dare's server with only a security confirmation prompt. So the problem doesn't exist in IE (I'm not sure it's as secure though).

Update (2006/05/16): I updated the script to emulate more XMLHttpRequest methods, including setRequestHeader and getResponseHeader. Let me know if you need more methods supported.

Update (2006/05/19): After working with another Microsoftie, Yaron Goland, who needed this script, investigating why it was not working for him, it seems that there is a problem. I suspect its related to the GM 0.6.4 security model. Basically, when the callback (onreadystate) is called, it doesn't appear to be in the right context and it cannot even see global variables from the script...
I'll dig some more, but consider this script back in a "alpha" stage at this point. It may turn out that a full blown Firefox extension is needed for this...

Update (2006/05/22): I received more feedback confirming that the script works. It also works on my own test page. I haven't figured out why the problem I described above is occuring with Yaron's page.


Minor user scripts:

Bloglines - Open Links in New Tabs makes it easier to open tabs while using Bloglines: simply (left-)click on the post titles, instead of middle-clicking on them.

This one attempts to fix the page titles on Dave Pollard's blog, "How to Save the World". Clean and meaningful titles really make it easier to bookmark/email/tag pages.

Here's a bunch tiny scripts to clean the titles on various other sites:

Posted by Julien on September 02, 2005. Permalink
Comments
comments powered by Disqus

I get the following error when using this script.

'GM_xmlhttpRequest is not a function'

What am I doing wrong?

A.

Posted by: Andy Hume at October 11, 2005 05:43 AM

Maybe you might indicate than greasemonkey is a plugin for mozilla. And it has to be installed before you run the script.
With this point of view your solution is equivalent to tell to the end user than he has to change his browser to view the page he has requested ...

There is no magic solution to pass thru the mozilla security restrictions.

Posted by: Matthieu Peschaud at December 13, 2005 02:51 AM

Matthieu,

You're right, but you seem to forget one thing: this is not meant for end users, but for developers.

Posted by: Julien Couvreur at December 13, 2005 08:19 AM

mmm... ok ok

when you develop something, it is supposed to be for somebody. By "end user", i mean this person who will use your script.

If the end user as enought knowledge to be abble to install greasemonkey plugin, he will also be abble to push down the mozilla security restrictions, which is the simpliest solution I think.

I mean you are presenting your solution as an easy way to pass thru the mozilla security restrictions, but it is quite ambiguous around the fact tahn it is based upon greasemonkey.
And If you don't know what is greasemonkey, it seems to be a marvelous solution !
Look at Andy Hume's comment ... it is exactly the problem that I am talking about.

Here was my point of view :)

Matthieu.

Posted by: Matthieu Peschaud at December 13, 2005 08:50 AM

mmm... ok ok

when you develop something, it is supposed to be for somebody. By "end user", i mean this person who will use your script.

If the end user as enought knowledge to be abble to install greasemonkey plugin, he will be abble to push down the mozilla security restrictions.

I mean you are presenting your solution as an easy way to pass thru the mozilla security restrictions, but it is quite ambiguous around the fact tahn it is based upon greasemonkey.
And If you don't know what is greasemonkey, it seems to be a marvelous solution !
Look at Andy Hume's comment ... it is exactly the problem that I am talking about.

Here was my point of view :)

Matthieu.

Posted by: Peschaud Matthieu at December 13, 2005 08:52 AM

Andy, you need to use a newer version of Greasemonkey.

Posted by: Julien Couvreur at December 18, 2005 11:14 AM

I just updated the script to run in Firefox 1.5 and Greasemonkey 0.6.4. Older versions are not supported anymore.

Posted by: Julien Couvreur at January 13, 2006 10:51 AM

Andy, you need to use a newer version of Greasemonkey.

Posted by: resimler at February 2, 2006 08:32 AM

This is a great greasemonkey script. I'm trying to write a Mac OS X Dashboard Widget and needed to have my XMLHttpRequest access an external URL. Is there anyway I can get this user script with setRequestHeaders and the ability to send data for posting?

Thanks.

Posted by: techniq at May 14, 2006 09:46 PM

@Matthieu: I think what Julien means is that the script is intended to allow you to run your xmlhttprequest .js files locally, and still be able to connect to your (non-local) server-side code. Hence it is a useful tool during development.

However, once you upload the scripts, you can use the regular Mozilla request to access your server code, since it will be on the same site.

So, the end user most definitely doesn't need to use Greasemonkey, or even know what it is.

One thing I'm curious about though (I'm just starting to teach myself AJAX): does the Mozilla security problem also affect a script that's trying to access server code from another site, or is it just a problem for local (i.e. C: drive) testing?

Posted by: Louis at May 18, 2006 07:55 AM

Hey!

I am not able to digest the sample code give by you!

Please guide me how to make use of it. I am having issues with

xmlhttp.open("POST","https://www.ups.com/ups.app/xml/Rate?",false);

in my code! and get error as Permission denied.

Any suggestion??

Kind Regards
Yoshita

Posted by: yoshita at June 18, 2006 11:31 PM

If thought that was a magic solution too, but if it's for developers only, i think it's way too complicated to set up; here is a lighter solution:

in mozilla based web browser just type 'about:config' in the adress, and then turn the field 'signed.applets.codebase_principal_support' to true

Now if you have the permission denied again just include in top of the failing instruction the following code:
'try {netscape.security.PrivilegeManager.enablePrivilege(\'UniversalBrowserRead\');} catch(e) {}'

it works, i use it

If think the only magic solution for end-user is to sign our web-api

Posted by: Shaft at June 28, 2006 03:05 AM

Shaft, if you need cross-domain XMLHttpRequest for end-users, you can use Flash.
See my FlashXMLHttpRequest project at
http://blog.monstuff.com/FlashXMLHttpRequest

Posted by: Julien Couvreur at July 2, 2006 05:31 PM

I originally started out with "remote scripting" and "cross-domain requests" and "single page applications" long before I knew what they were...

Originally programmed in ASP (IIS) using a free API called ASPTear, I wrote some pages for personal use that pulled information from different websites, did a text-extract, and just dumped it back out as PRE formatted text. In those days, we didn't HAVE CSS -- and we wrote webpages on the sides of caves with sticks.

Anyway, when I lost access to the IIS server I was using, I re-wrote the ASP pages as .HTM vbscript to run in IE, from my desktop/harddrive. The pages expanded to 9, and work well, but I really want to re-write them so they'll run in most JS-standard browsers.

It will take me a while, but it's time to modernize, add more CSS, convert more extraction routines to use RegExp, etc. I am looking forward to the conversion, but the first step already had me hosed... CROSS-DOMAIN JS... I am hoping the hints here will help me.

Strangely, I had absolutely NO problem doing Cross-Domain in IE using the ActiveX XML API.

In case anyone is wondering, the original SPA was used to pull job ads from various online sources, then expanded to pull random jokes from the Internet humor Newsgroups, then...

Well, today it's a start page with random Photo Of The Day pictures, weather, quotes from the Andy Griffith Show, various pundits, DHS Threat Advisory Status, Moon Phase, personal links and search feeders for Half and Amazon... and that's just the start page... the rest are... 8 pages of comics pulled from multiple sites. It was fun to write, and sure makes my comic reading easier and faster, but IE is too limiting. Time to expand the horizons.

Thanks for the page.

Posted by: Marc at July 30, 2006 07:10 PM
Trackbacks