You are on page 1of 3

Preventing Page Review after Logout with Forms Authentication

page
by Richard Dudley
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 94323/ 533

Introduction
The inclusion of Forms Authentication in the .NET Framework has made it significantly easier to
protect web pages by requiring users to log in to a web application. One problem with Forms
Authentication is browser caching of secured pages. After a user logs out of an application, they
can still review secured pages by using the browser's Back button. The user is only looking at local
copies of pages they viewed while previously logged in, but this can be undesirable in many
instances, and present a genuine security risk.

In this article, we will explore a few small coding changes you can make to prevent users from
reviewing secured pages after logout.
Preventing Review of Secured Pages
In order to speed up the Internet experience and reduce load on web servers, all browsers keep
locally cached copies of the visited pages for a specified amount of time. Users can set a time limit
or space limit in their browsers for the page cache, but few do. When a browser requests a page
from a web server, it checks the timestamp on that page to determine whether the content has
changed. If the timestamps match, the browser renders the local copy; if the timestamps differ, the
browser requests the page again. For the site visitor, it's usually good that the browser has a local
copy of any page that does not change frequently, since it's faster to load locally than download
everything again. However, for the webmaster, this can be a problem. When users log out of a site
protected by Forms Authentication, they may still review the local copy of the pages they visited.
This can be a security issue, as well as a nuisance. The timestamp of pages with dynamic content
does not change when the page is loaded, since the timestamp is read from the file system, not set
when the page is requested (note that pages with querystring parameters are not usually cached by
browsers, as it is evident they are dynamic). As such, browsers may keep displaying the same old
content over and over, instead of requesting fresh content.

Another security problem can arise if proxy servers are in use. When a browser requests a page
through a proxy server, the proxy server compares the timestamp of the requested file to see if it
has changed. If the timestamps match, the proxy server sends its locally cached copy back to the
browser. In the past, if two users requested the same page, they may have received identical copies
of the page. If the page contained dynamic content for one user, another user may have seen
something they should not have seen. Most proxy servers seemed to have remedied this issue by
comparing the user who requested the page, but that doesn't mean every ISP or corporation is
using a recent version of its proxy server.

Fortunately, web programmers can send three HTTP headers to the browser that specify how long a
page should be cached by the browser or proxy server, if at all. These headers can be set in the
HTML code, or set in directly in server-side code. The three useful headers are Expires, Pragma:
no-cache, and Cache-Control.
HTTP Headers Described
Information on using all three headers and how Internet Explorer supports them can be found at
http://support.microsoft.com/kb/q234067/.

Expires

The Expires header is part of both the HTTP 1.0 and 1.1 specifications, and should be supported by
every major and minor browser in use. Per the specification, the Expires header does not prevent
local caching of the resource, but rather tells the browser to check for a new version after a
particular date. The Expires header is set to either an absolute date and time to indicate the
expiration of the page's content, or a value of -1 to indicate immediate expiration. For your
dynamically generated pages, it's suggested to set Expires to a value of -1; for static pages, or ones
that change infrequently (daily or longer), it's suggested to set Expires to a specific date value
slightly before the next expected update.

Also per the specification, using the browser's Back button should display pages even past their
expiration date. As such, setting the Expires header is not enough, but every page should have an
Expires value.

Pragma: no-cache

This header is part of the HTTP 1.0 and 1.1 specifications. The Pragma: no-cache header is not
meant to control browser caching of server responses, but is intended to signal proxy servers to
expire the request and properly forward any other similar requests to the web server.

Recent versions of Internet Explorer support the use of Pragma: no-cache to expire responses, but
this implementation is not supported by version 5 or earlier, and may not be supported by many
other browsers, either. You may see this header in a number of examples online, but since it is
actually part of the request, it is recommended you do not use it in the response stream.

Cache-Control

This header is part of the HTTP 1.1 specification only. Most browsers and proxy servers in use
today should support HTTP 1.1, but the browser option can be configured by the user. It's probably
safe to assume that devices contacting your site support this header, but you cannot count on that to
be the case.

You can set Cache-Control to several values:

• Public: Content can be stored in public shared caches. Good for your home page and any
other publicly available pages.
• Private: Content can be stored only in private cache. Proxy servers should not store the
content unless they support private caching. Useful for pages with user interaction; a better
method is No-store (see below).
• No-cache: Content may not be cached. This is the highest security setting, and should be
used for all pages that contain sensitive information.
• No-store: A better form of Private. Content may be cached for the length of the session, but
not archived.

All applications should set the Cache-Control of their pages.


Setting HTTP Headers in HTML
Any of these three headers can be set in the <head> section of your web page, as shown below:

Listing 1: HTML HTTP Header Tags


<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
<meta http-equiv="CACHE-CONTROL" content="NO-CACHE">

You will need to add these headers to every page, and set their values accordingly. Internet
Explorer versions 4 and 5 do not support the Cache-Control http-equiv tag, so Microsoft
recommends setting this value as part of the Response object in server-side code.
Setting HTTP Headers in Server-side Code
In ASP.NET, a class named HttpCachePolicy has been introduced, which gives finer control over
expiration and caching of content. It is recommended that a developer use the HttpCachePolicy
class rather than the Response.AddHeader() method to set caching policies.

Listing 2: HttpCachePolicy Properties


Response.Cache.SetExpires(DateTime.Parse("6:00:00PM"))
Response.Cache.SetCacheability(HttpCacheability.Private)

Note that the SetExpires() method only accepts a DateTime value. Instead of passing -1 as a value,
you will need to pass an absolute time (as above), or use the DateTime.Now property to pass in the
current time. According to the HTTP specifications, all absolute times in these headers should be
set by Greenwich Mean Time to avoid any strange behavior when crossing time zones.

Additional properties and methods for the HttpCachePolicy class are detailed in the documentation
for the 1.1 Framework and Beta 2 Framework.

An easy way to ensure these properties are set on every page is to create an abstract page class that
inherits from the intrinsic Page class, and set these properties in the Page_Load or
Page_Init handler of the abstract page class. Then, every page in your application should inherit
the abstract page. This is a much easier way than copying HTTP headers into every ASPX page.
Summary
A simple coding change to a base page class can help ensure users cannot review pages after they
log out of a website protected with Forms Authentication. When used with Session.Abandon and
proper expiration of the FormsAuthenticationTicket, you add additional security to your site, and
help protect your user’s information.

You might also like