Odd Firefox Cookie Behavior
Firefox’s behavior with regard to sending HTTP cookies doesn’t seem to be fully standards-compliant. I don’t think this has been posted before — a quick search of the Mozilla bug database didn’t turn up anything — but if this has been encountered before I’ve love to know.
On Cookies
HTTP cookies consist of named chunks of data. They’re used as a way to graft session state on top of HTTP. (HTTP is a stateless protocol, which means that any given transaction between a client and server is independent of any other transaction between a client and server.) Cookies are stored client-side (i.e. a Web browser) and are transmitted to the server when necessary.
The implementation of cookies that just about every browser uses is specified in RFC 2965.
Cookies contain several metadata fields that instruct a client when it should consider a cookie “valid” and when it should send a given cookie to the server. For example, a cookie records which domain(s) it should be sent for, its maximum age before it’s considered stale and should be discarded, if it should only be sent to the server in an encrypted channel, and so forth.
The bit of metadata we’re concerned with here is a piece of metadata called the path.
On Cookie Paths
The cookie path, along with the cookie domain, defines a set of URLs for which the cookie should be sent to the server. Section 3.3.4 of RFC 2965, “Sending Cookies to the Origin Server”, states
The user agent applies the following rules to choose applicable
cookie-values to send in Cookie request headers from among all the
cookies it has received.
…
Path Selection
The request-URI MUST path-match the Path attribute of the cookie.
The definition of “path-match” is given in section 1:
For two strings that represent paths, P1 and P2, P1 path-matches P2
if P2 is a prefix of P1 (including the case where P1 and P2 string-
compare equal). Thus, the string /tec/waldo path-matches /tec.
From this definition, we can devise a few examples:
/foo/barpath-matches/foo/foo/barpath-matches/foo//foodoes not path-match/foo/(/foo/is not a prefix of/foo)
It’s that last example that I want to talk about.
Firefox
I set up a small PHP script at http://tinderbox.ninjawedding.org/cookies/ that just sets cookies. Here’s the full code of the script.
<?php
if ($_COOKIE["set"] == "") {
setcookie("set", "HAS BEEN SET", 0, "/cookies/", "tinderbox.ninjawedding.org", false, true);
}
echo("The cookie named 'set' has the value " . $_COOKIE['set'] . ".");
?>
Not much to it. The parameters of setcookie, in order, instruct the browser to:
- make a cookie named “set”
- …and stick the value “HAS BEEN SET” into it
- …and only let the cookie stick around until you close your browser (0 means “expire at end of session”)
- …and only send it for URLs path-matching
/cookies/ - …and send it if the domain is
tinderbox.ninjawedding.org - …and send it for any type of HTTP connection, encrypted or otherwise
- …and send it only for HTTP connections
If we visit this script once with Firefox 3.0.4, we’ll have the cookie set. So far, so good. If we now go back to http://tinderbox.ninjawedding.org/cookies/, we’ll see "HAS BEEN SET" appear on the screen, meaning that the cookie was sent for http://tinderbox.ninjawedding.org/cookies/.
But now break out a network sniffer — I like Wireshark — and go to http://tinderbox.ninjawedding.org/cookies. My lighttpd configuration on tinderbox will cause an immediate redirection back to http://tinderbox.ninjawedding.org/cookies/, so you’ll see that redirection going on, but there’s something extra in there:

Firefox is sending the “set” cookie for /cookies and /cookies/. I’m pretty sure this isn’t the right thing to do, based on RFC 2965 and the fact that WebKit, or at least recent builds, do not behave like this. If you perform the Wireshark experiment with a WebKit-based browser (like, say, Safari), you will not see the Cookie header being sent for /cookies.
How I Found This
At work, we use CAS, which is a single sign-on system for Web applications. One of our applications using CAS had a small bug that caused it to send a request to cas_base_url instead of cas_base_url/login. This was fixed, but the problem was masked for a while.
The reason why is two-fold:
- The CAS server protocol specification does specify the behavior of some URLs, such as
/loginand/logout, but it leaves/(thecas_base_url) unspecified. The CAS server we use treats requests to / as if it were a request to/login. - Our CAS server doesn’t redirect to a URL with a trailing slash if a URL without a trailing slash is sent. I think it’s some Apache rewrite rule configuration that’s not quite set up right, but I’m not really sure. At any rate, putting a trailing slash on a CAS URL doesn’t do the right thing in our setup; you want to elide the slash.
Firefox would send the CAS cookies to cas_base_url when the aforementioned application send a request to it. This made the application log in fine under Firefox. But under Safari — which didn’t send the cookies — this didn’t work at all. In fact, in the absence of cookies, the CAS server treated it as if though the user never logged in.
Is This Really A Problem?
I don’t know. From a pure follow-the-standards perspective, I think it is a problem (though, of course, I’d love to be proven wrong, too). From a more pragmatic perspective, though, it’s hard to say.
For example, I can gin up some scenarios in which it could be a security problem — say that http://www.example.com/foo and http://www.example.com/foo/ are owned by different entities, and you only trust one of them — but I’ve never seen that happen in practice.
Conclusion
I think Firefox 3.0.4’s cookie handling is incorrect and should be fixed.
I don’t know if this bug has been reported. As I said at the beginning, one Bugzilla search didn’t turn up anything, but maybe a variation on that search (or a totally different one) would strike gold. Let me know if I’ve missed something.
If nobody can find anything, I guess I’ll report it.