Skip Navigation LinksHome > View Post

Better understand Cookies with ieHTTPHeaders

ieHTTPHeaders is an amazing extension for Internet Explorer and it's available for free (though the author does accept donations) and it is now an essential part of my web developer toolkit. It's helped me out of a number of tricky situations, allowing a quick peek into the conversations that take place between a browser and a server that often go unnoticed.

In this article we'll cover the following:

  • The ieHTTPHeaders utility and how to use it to help you debug problems
  • Cookies
  • Forms Authentication tickets, user data and encryption
I have been lucky/unlucky [delete as applicable] enough to have been working with web applications long before the days of ASP.NET. Back in the day, web developers would regularly have to get their hands dirty with HTTP plumbing. Many developers today are blissfully unaware of what is happening 'on the wire' and this is entirely understandable with the provisions available in the modern development environment. However, getting to know this beast can be a killer string to your bow. So let's walk through a recent problem I had with Forms Authentication that ieHTTPHeaders and this knowledge helped me solve.
If you're unfamiliar with Forms Authentication in .NET 2.0 check out this quickstart tutorial.
Just recently I was working on a new ASP.NET project that used Forms Authentication. We wanted to use the FormsAuthenticationTicket to encrypt and store an extra piece of data in the cookie. Let's imagine that the application lived in a virual directory called FormsAuthenticationTest directly off the root of the server.

Since we are good boys, we set the forms authentication cookie path appropriately in the web.config so that the cookie is not shared with other applications.

<authentication mode="Forms">
    <forms
        name=".COOKIENAME"
        loginUrl="login.aspx"
        path="/FormsAuthentication" />
</authentication>

Now let's spice things up a little. Using Forms Authentication is a doddle, but if you want to use some of the more advanced features, such as user data, things get a little more complicated.

Forms Authentication cookies are encrypted using Triple DES to prevent malicious clients from tampering with or reading the cookie. Fantastic! Especially if we can leverage this for another purpose. We can use this secure space to store other little tit-bits of information for re-use later, such as the user's role.

Note: This isn't going to be an ideal solution for everyone and you should assess the security risk of such activities before jumping on this bandwagon. It may suit you, it may not - the choice is yours and so is the responsibility.
The next step is to create our own ticket, using the FormsAuthenticationTicket class:

private string GetUserRole(string username)
{
    // imagine we go away to a database here
    // to determine the role of the user...
    return "Admin";
}

protected void _btnLogin_Click(object sender, EventArgs e)
{
    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
        1, // The cookie version
        _txtName.Text, // The user name
        DateTime.Now, // Time of issue
        DateTime.Now.AddMinutes(20), // Time of expiry (20 mins)
        false, // Should the cookie be persistent?
        GetUserRole(_txtName.Text) // Where we put the extra stuff
        );

    string encryptedTicket = FormsAuthentication.Encrypt(ticket);

    // Now we need to add the cookie to the response, using the
    // cookie name in the web.config
    Response.Cookies.Add(
        new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedTicket));

    // Finally, redirect to the appropriate Url.
    Response.Redirect(FormsAuthentication.GetRedirectUrl(_txtName.Text, false));
}

You should find that this works a treat, it did for me. Until I tried to logout, which should be even easier:

protected void _btnLogout_Click(object sender, EventArgs e)
{
    FormsAuthentication.SignOut();
}

Sure, clicking the Logout button took us to the login screen as expected, but if I navigated the rest of the site it was clear that I was still logged in - the cookie must still exist!?

I smelled a fish. And fish and cookies just do not go.

Time to have a look at what ASP.NET is saying to my browser. If you haven't installed ieHTTPHeaders yet, now is probably a good time to do so.

Once installed, it's easy-peasy to use. Open Internet Explore and go to View > Explorer Bar > ieHTTPHeaders to display the frame in which you'll see the output, as shown below. Now start surfing the web and you'll see a plethora of headers streaming through the window.

Note: After install you may have to close all IE windows before the plug-in appears. Failing that, try a reboot.
Let's look in more detail at the exchange between the browser and the web application that seems to be serving up dodgy cookies.

I've copied the output from ieHTTPHeaders into the table below with an explanation on the right giving a brief summary of what it all means.

HTTP Header (interesting bits in bold) What's going on?
GET /FormsAuthenticationTest/default. aspx HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Accept-Language: en-gb Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50215) Host: localhost Connection: Keep-Alive This is the browser requesting the default.aspx page at the root of the web application.

Note that the browser also tells the server what it is (User-Agent), what file types it accepts and much more.

HTTP/1.1 302 Found Server: Microsoft-IIS/5.1 Date: Sat, 15 Oct 2005 15:22:18 GMT X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50215 Location: /FormsAuthenticationTest/login.aspx ?ReturnUrl=%2fFormsAuthenticationTest %2fdefault.aspx Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 204 The server replies, with a 302 response. This is a "Redirect" instruction to the browser. "This isn't the page you're looking for. Move along. Try login.aspx"
GET /FormsAuthenticationTest/login.aspx ?ReturnUrl=%2fFormsAuthenticationTest %2fdefault. aspx HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Accept-Language: en-gb Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50215) Host: localhost Connection: Keep-Alive The browser, being a nice browser, does what it's told and requests the specified page.
HTTP/1.1 200 OK Server: Microsoft-IIS/5.1 Date: Sat, 15 Oct 2005 15:22:18 GMT X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50215 Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 726 "Oh, you want the login page? No problem, Here you go!"
POST /FormsAuthenticationTest/login.aspx ?ReturnUrl= %2fFormsAuthenticationTest%2fdefault.aspx HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost/ FormsAuthenticationTest/login.aspx? ReturnUrl= %2fFormsAuthenticationTest %2fdefault.aspx Accept-Language: en-gb Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50215) Host: localhost Content-Length: 114 Connection: Keep-Alive Cache-Control: no-cache __VIEWSTATE=%2FwEPDw...snip...kYLBz4& _txtName=Josh& _txtPassword=password& _btnLogin=Login Now, the user has entered their credentials and posted it up to the server. You can see the body of the post emboldened at the bottom of the page.

This post would cause the _btnLogin_Click event above to execute.

HTTP/1.1 100 Continue Server: Microsoft-IIS/5.1 Date: Sat, 15 Oct 2005 15:30:40 GMT X-Powered-By: ASP.NET You'll see a lot of these. It's essentially the server saying ";OK, got your request. Just hang on a minute and I'll get you a response".
HTTP/1.1 302 Found Server: Microsoft-IIS/5.1 Date: Sat, 15 Oct 2005 15:30:40 GMT X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50215 Location: /FormsAuthenticationTest/default.aspx Set-Cookie: .ASPXAUTH=A90B1E...snip...69C5E9; path=/ Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 154 Right, now we're on the money!

Look! The server is trying to set the encrypted authentication cookie using the Set-Cookie command. Also, as expected, the server is redirecting us back to our original destination.

Note the ";path=/"; section of the Set-Cookie directive. Wait a minute - there's something wrong there! We'll discuss what later.

GET /FormsAuthenticationTest/default.aspx HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost/ FormsAuthenticationTest/login.aspx ?ReturnUrl=%2fFormsAuthenticationTest %2fdefault.aspx Accept-Language: en-gb Cookie: .ASPXAUTH=A90PXI...snip...832RE9 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50215) Host: localhost Connection: Keep-Alive Cache-Control: no-cache Following instructions again, the browser requests the designated page and look!

The browser has remembered our cookie and will now send it back to us every time. Yeah!

HTTP/1.1 200 OK Server: Microsoft-IIS/5.1 Date: Sat, 15 Oct 2005 15:30:40 GMT X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50215 Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 962 Finally we get to our original page. The server delivers the default.aspx page with a cheerful 200 OK Code.

It always amazes me how many exchanges the client and server have in a simple Forms Authentication scenario. This is nothing to worry about - it all happens very quickly and your users will be blissfully ignorant.

So, what went wrong when we tried to logout later? Lets look at the header returned from the server after the logout request was issued.

HTTP Header What's going on?
HTTP/1.1 302 Found Server: Microsoft-IIS/5.1 Date: Sat, 15 Oct 2005 16:05:58 GMT X-Powered-By: ASP.NET X-AspNet-Version: 2.0.50215 Location: /FormsAuthenticationTest/login.aspx ?ReturnUrl=%2fFormsAuthenticationTest %2fDefault.aspx Set-Cookie: .ASPXAUTH=; expires=Mon, 11-Oct-1999 23:00:00 GMT; path=/FormsAuthenticationTest; HttpOnly Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 1159 The server is re-issuing the Set-Cookie command. But this time the cookie is empty and set to expire back in 1999. This is how servers tell browsers to delete a cookie.

Note that the cookie path is "/FormsAuthenticationTest". Uh-oh.

The clue to our problem was in the server's original Set-Cookie instruction. We have specified in the web.config that the cookie path should be "/FormsAuthenticationTest" but when the original cookie is issued it is issued to the root path "/". Why?

Simple! Because we're issuing the cookie ourselves. Remember this piece of code in our login routine?

Response.Cookies.Add(
new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedTicket));

It seems obvious now, but because we're taking responsibility for issuing the cookie away from the FormsAuthentication class, we must set the cookie path ourselves otherwise the default root path is assumed. When the cookie is later deleted by FormsAuthentication.SignOut(), it targets a cookie with a different path and the removal doesn't go quite as expected thus logout fails. Eureka! And the fix?

    HttpCookie cookie = new HttpCookie(
    FormsAuthentication.FormsCookieName, encryptedTicket);
    // get the path from config
    cookie.Path = FormsAuthentication.FormsCookiePath;
    Response.Cookies.Add(cookie);

Now I just need to workout how to get the cookie path from the authentication section in web.config to avoid having to specify it twice :o)

One last tip before we part company: When using ieHTTPHeaders you may find it useful to disable images in your browser, this will significantly reduce the amount of 'noise' you have to wade through.

Good luck and enjoy!

Josh

Tags: ASP.NET

 
Josh Post By Josh Twist
12:22 PM
16 Nov 2005

» Next Post: XHTML in .NET 2.0
« Previous Post: SQL Profiler with .NET

Comments are closed for this post.

Posted by charlie arehart @ 17 Aug 2006 4:12 PM
Thanks for the nice intro to the tool. Every other entry i was finding while searching just left it as "an add in to the IE explorer toolbar". Yours was the first I saw to explain that this mean view>explorer bar. I had been looking for something on the toolbar.

If I may say so, Fiddler (free, from Microsoft, at http://www.fiddlertool.com/) really does a superior job of organizing the information that iehttpheaders just dumps at the bottom of the screen. There's a whole lot more information there about it than at Mr Blunck's spartan site--though as a free tool from one person it's understandable.

Back to my first point, Fiddler also installs itself as an icon on the IE toolbar, which is nice. But for many more reason, I will recommend folks check it out, too.

on the other hand, does indeed . Better still, it

Posted by charlie arehart @ 17 Aug 2006 4:16 PM
Pardon the last lines there. Leftover from some editing of the comment and I forgot to delete them. Also, in case it's not clear, I meant to say "there's a whole lot more info about Fiddler at its site than there is about ieHTTPheaders at Mr. Blunck's site."

Posted by Josh @ 18 Aug 2006 12:22 AM
Hi Charlie,

Thanks for getting in touch. I'm a big advocate of Fiddler too and have linked to it in a few posts. However, I do love the speed and ease with which I can flick ieHTTPHeaders on to just have a nosy at what's going on.

I often use it if I want to see what platform a server is running on. Firing up fiddler just feels a bit more 'intrusive' so I save it for those hardcore debugging jobs.

Posted by roger @ 25 Aug 2008 7:01 AM
nice

Posted by roger @ 25 Aug 2008 7:01 AM
nice

Posted by rerer @ 25 Aug 2008 7:02 AM
nice

© 2005 - 2014 Josh Twist - All Rights Reserved.