Cookies with and without the Domain Specified (browser inconsistency)

C#CookiesCross Browser

C# Problem Overview


I've noticed that there are some real inconsistencies between browsers in terms of cookies.

This is going to be rather long so bear with me.

Note: I've setup a domain in my host file called "testdomain.com", this bug WONT work when using "localhost".

Note2: I am curious to know how this works on Apache/PHP if when you retrieve a cookie by name if it gives a collection of cookies back.

Wikipedia

Wikipedia states that: http://en.wikipedia.org/wiki/HTTP_cookie#Domain_and_Path

> Domain and Path
> The cookie domain and path define the scope of the > cookie—they tell the browser that cookies should only be sent back to > the server for the given domain and path. If not specified, they > default to the domain and path of the object that was requested.

So if we push down:

Response.Cookies.Add(new HttpCookie("Banana", "2")
{
                
});

We should get a cookie with the domain used being the domain from the requested object, in this case it should be "testdomain.com".

W3

W3 states in the specification for cookies: http://www.w3.org/Protocols/rfc2109/rfc2109

> Domain=domain > > Optional. The Domain attribute specifies the domain for which the > cookie is valid. An explicitly specified domain must always start > with a dot.

So if we push down:

Response.Cookies.Add(new HttpCookie("Banana", "1")
{
    Domain = Request.Url.Host
});

We pushed down the host-name explicitly, we should get a domain name set on the cookie which would be prefixed with the dot, in this case it should be ".testdomain.com".

It also states what's on Wikipedia:

> Domain Defaults to the request-host. (Note that there is no dot at > the beginning of request-host.)


With me so far?

If I use the first method, defining a Domain:

Response.Cookies.Add(new HttpCookie("Banana", "1")
{
    Domain = Request.Url.Host
});

This is the results:

IE9: 1 cookie

IE with 1 cookie and domain explicitly set

Opera: 1 cookie

Opera with 1 cookie and domain explicitly set

Firefox: 1 cookie

Firefox with 1 cookie and domain explicitly set

Chrome: 1 cookie

Chrome with 1 cookie and domain explicitly set

As you can see, both Opera and IE both set an EXPLICIT domain without the dot prefix.

Both Firefox and Chrome DO set the EXPLICIT domain with a dot prefix.

If I use the following code:

Response.Cookies.Add(new HttpCookie("Banana", "2")
{
              
});

IE / Opera: Both have the exact same result, the domain WITHOUT the dot prefix.

Funnily enough, Firefox and Chrome both create cookies WITHOUT the dot prefix.

(I cleared all cookies and ran the code again)

Firefox:

Firefox with 1 cookie and domain explicitly set

Chrome:

Chrome with 1 cookie and domain explicitly set

INTERESTING BIT

This is where it gets interesting. If I write the cookies one after another like so:

Response.Cookies.Add(new HttpCookie("Banana", "1")
{
    Domain = Request.Url.Host
});
Response.Cookies.Add(new HttpCookie("Banana", "2")
{
                
});

PERSONALLY I would expect one cookie to exist in the browser, because I assume it's based on the cookie name.

Here's what i've observed:

In IE / Opera, the LAST cookie set is the cookie that is used. This is because the Cookie name and Domain name are identical.

If you explicitly define a domain name with a dot, both browser will still see 1 cookie, the last cookie of the same name.

Chrome and Firefox on the other hand, see more than 1 cookie:

I wrote the following JavaScript to dump the values to the page:

<script type="text/javascript">

(function () {
    var cookies = document.cookie.split(';');
    var output = "";

    for (var i = 0; i < cookies.length; i++) {
        output += "<li>Name " + cookies[i].split('=')[0];
        output += " - Value " + cookies[i].split('=')[1] + "</li>";
    }

    document.write("<ul>" + output + "</ul>");
})();

</script>

These are the results:

IE - 2 cookies set (browser sees 1):

IE - 2 cookies set, the outcome

Opera - 2 cookies set (browser sees 1):

enter image description here

Firefox - 2 cookies set and browser sees 2!:

enter image description here

Chrome - 2 cookies set and browser sees 2!:

enter image description here


Now you're probably wondering wtf all this is.

Well:

  1. When you access the cookie by Name in C#, it gives you 1 cookie. (the first cookie that has that name)
  2. The browser sends ALL cookies to the server
  3. The browser doesn't send any information other than the key/value of the cookie. (this means the server doesn't care about the domain)
  4. You can access both cookies of the same name, if you retrieve them by index

The problem...

We had to change our Authentication to specify the domain in the cookie when we pushed it down.

This broke Chrome and Firefox, users were no longer able to login, because the server would try authenticate the old auth cookie. This is because (from my understanding) it uses the Authentication Cookie Name to retrieve the cookie.

Even tho there are two cookies, the first one is retrieved which happens to be the old one, authentication fails, user isn't logged in. SOMETIMES the correct cookie is first in the list, and the authentication succeeds...

Initially we solved this by pushing a cookie with the old domain to expire it. This worked in Chrome and Firefox.

But it now broke IE/Opera since both browsers don't care about the domain and only compare the cookie based on the name.

My conclusion is that the domain on a cookie is a complete utter waste of time.

Assuming that we must specify the domain, and we can't rely on users to clear their browser cache. How can we resolve this problem?

Update:

Digging into how .NET signs a user out.

if (FormsAuthentication._CookieDomain != null)
{
	httpCookie.Domain = FormsAuthentication._CookieDomain;
}

It looks like it's entirely possible for the Forms authentication to push an expired Auth cookie, that is entirely unrelated to the cookie the user is authenticated with. It doesn't use the current Auth Cookie's domain.

Which it can't use anyway, since the domain isn't pushed back to the server with the cookie.

Update 2

It seems FormsAuthentication is really broken. If you use an explicit domain name on a cookie when you authenticate the user, wait for the session to timeout, then refresh the page, the method of generating the cookie used by FormsAuthentication results in the domain being null which causes the browser to assign a dotless domain.

It requires that Forms be assigned a domain up front for it to be assigned to the cookie, this breaks a multi-tenant system...

C# Solutions


Solution 1 - C#

@WilliamBZA's suggestion helped solve the initial problem, but then signout/session timeout bug that results in the cookie creating an implicit domain cookie has made me come to the conclusion that the solution is...

Don't use Explicit cookies in .NET... ever

There are far too many problems, sure they can be solved by being explicit on the Form/Domain, Cookie/Domain, etc. To ensure that the correct domain is used everywhere. But if your application hosts multiple domains or is multi tenant, then it just becomes too problematic.

Lesson is learnt. Don't use explicit cookies.

Solution 2 - C#

Can't help with why the cookies are treated differently, but a quick fix would be to use a different cookie name per sub-application rather than using the domain of the cookie.

In the case of Forms Authentication, change the name of the ASPXAUTH cookie.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPhillView Question on Stackoverflow
Solution 1 - C#PhillView Answer on Stackoverflow
Solution 2 - C#WilliamBZAView Answer on Stackoverflow