Some time ago, I had to find a way to strip cookies from XMLHttpRequests in Mozilla Firefox. It took me a while to figure it out, so I thought it might be a good idea to share my results.
What we want to do
The code to use the cookie monster will be as follows:
1 2 3 4 5 6 7 8 9 10
Sounds easy enough, eh? So let’s start to create our cookie monster.
A Word of Note
One might think that the easiest way to remove cookies from XMLHttpRequest would be to directly modify the HTTP headers of the request, for example by using setRequestHeader() as seen at the Mozilla Developer Center (see also Using XMLHttpRequest). However, the following code will not work.
1 2 3
There are several reasons why this intuitive approach will get you nowhere.
- First, the “setRequestHeader()“ method of the XMLHttpRequest object will actually append cookies to the request. It will not replace and thus not remove them.
- Second (and this took me a while to figure out), the way that cookies are added to XMLHttpRequests nullifies the approach. I haven’t looked at the actual source code, but it seems that cookies are attached to requests at a later stage.
Ok, maybe this sounds a bit too fuzzy. What basically happens is that when we try to remove the cookies by calling
setRequestHeader(), the cookies have not yet been included to the request. Messing around with the HTTP headers will be pointless at this time because all those pesky cookie HTTP headers which we want to remove in the first place will simply be addedafter we called
The correct way to implement our cookie monster is therefore slightly more complicated.
The basic idea is to use observers for getting notified when cookies are actually added to the request, and to usensIHttpChannel.setRequestHeader() to actually remove the cookies. Thus, the cookie monster will observe the assigned XMLHttpRequest and jump at its throat the moment it smells fresh cookies included in the HTTP headers!
The CookieMonster class will provide the following methods:
- “QueryInterface(iid)“ : method required to be compliant with the nsIObserver interface
- “observe(subject, topic, data)“ : removes the cookies on notification; the actual method definition is also required to be compliant with the nsIObserver interface
- “stopEating()“ : stop the cookie monster and do a clean up
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
We assign an XMLHttpRequest to our cookie monster. The monster will make sure that no cookie will ever make it to the server to which the request is sent.
First, we store a reference to the channel property of the XMLHttpRequest object. This channel provides an interface tonsIChannel / nsIHttpChannel, which in turn provides a slightly enhanced version of setRequestHeader(). We will use this method later on to actually remove (or eat, as you wish) the cookies, and thus solve problem #1.
Second and in order to solve problem #2, we have to give our cookie monster some assassination training and tell it to watch out for
http-on-modify-request events (lines 9-10). The
http-on-modify-request topic is triggered after the cookie data has been loaded into the request, but before the request is sent.
It is recommended to make sure that observers are removed when they are not needed anymore (to avoid memory leaks), especially when using strong references. This is the reason for line 14, where we make use of a small helper class,
Scheduler, whose purpose is to force the cookie monster to stop eating/watch for cookies after 15 seconds have passed. It also makes sure that the cookie monster will not wait forever in case the XMLHttpRequest simply does not have any cookies to be eaten. For the sake of simplicity, we will not look at the Scheduler class for now.
1 2 3 4 5 6 7 8 9 10
We need to implement a
QueryInterface() method so that the observer service from the previous code snippet knows that our cookie monster is able to observe topics, in this case
http-on-modify-request. If you have ever worked with observers before, this is nothing new and a pretty standard way to implement this required method. No magic here (and no cookies, unfortunately).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
observe method is the critical part. Here’s where we let the cookie monster eat up all cookies!
We check first if the notification sent from the observer service is matching the topic we’re interested in (
http-on-modify-request) and make sure that the notification corresponds to the channel of the assigned XMLHttpRequest. If so, we let the cookie monster lose: we use the slightly enhanced
setRequestHeader() method of the channel to remove all existing cookies. The important difference to
XMLHttpRequest.setRequestHeader() is the availability of a third parameter called
merge, which we set to false. To quote the XPCOM Reference: If [the HTTP header] value is empty and merge is false, the header will be cleared.
After we have removed all cookies, there is no need to watch out for new cookies, so we will stop scheduler (we are already done) and stop eating, as seen in lines 17-18.
1 2 3 4 5 6 7 8 9 10 11 12
The last method,
stopEating(), is used to end the life of our cookie monster because it has served its purpose. The cookie monster stops watching for cookies (line 7), and handles all instance variables over to garbage collection. I admit that we have coded a rather domestized version of the original cookie monster, but really, cleaning up is important nowadays (at least our cookie monster is still allowed to eat cookies).
That’s it! We have implemented a cookie monster which observes an XMLHttpRequest and removes all cookies from it. I hope it was as easy as promised.
You can download the code straight from my GitHub repository.
The code has been tested with Firefox version 1.5.x and 2.0.x.
It might work with Firefox version 3.x. However, there are some changes in 3.x that will require you to update the Cookie Monster code. For example, all of the relevant attributes of the nsIJSXMLHttpRequest interface, which in Firefox 2.0.x allowed you to monitor an HTTP request for progress updates, errors, etc., were moved to the nsIDOMProgressEvent interface in Firefox 3.1. Have a look at the Monitoring Progress section in Using XMLHttpRequest for instructions on how to update your code for Firefox 3.x.
The code is licensed to you under the GNU General Public License, version 2.
An alternative approach
Update 2011-09-25: Reader Ben Bucksch pointed out a different – and easier – method to prevent Firefox from sending cookies:
Given that it’s the cookie lib that’s overwriting our header, I just deactivate the lib. That’s fairly simple:
yourXMLHttpReq.channel.loadFlags |= Ci.nsIRequest.LOAD_ANONYMOUS;
See also the documentation for LOAD_ANONYMOUS:
1 2 3 4 5 6