s ASP programmers, you have probably received a request to password protect a portion of the Web site you are developing. The client wants the user to access pages A, B, and C?only after logging on with a valid user ID and password. One obvious mechanism to enforce this is to use the Web server’s security mechanism and control access to those pages, or even to a sub directory. Instead of allowing “anonymous” user access, which is the default, you can turn on Basic Authentication or Windows NT integrated authentication. The only problem with this scenario is that you need physical access to the Web server (and many Web developers have to toe the line with a Web master , DBA, or network administrator who “owns” the Web server). Secondly, you will need to create the user ID’s and passwords as user accounts on the local machine.
A second technique is to enforce authentication via an ASP page itself. For instance, you may have a login.asp page that asks for a user ID and password. Only if the user is authenticated against data in a database (which is much easier to maintain than NT user accounts), are they allowed to proceed to select pages. This technique has its own problems. Each page now needs to know the state of the login?has the user passed the authentication process in login.asp and is being redirected here, or is the user accessing Page B on its own merit, bypassing the login page altogether?
To handle this scenario, you need to maintain the login state. You can have a range of techniques to solve this issue?from simple to very complex procedures depending on how sensitive the information in the pages is. For example, if you were building an online banking application, I would recommend being very paranoid, and opting for a complex solution. However, if you are building less sensitive applications, a simple login state mechanism should be enough.
Two Common Techniques
So how does page A, B, or C know that a successful login has taken place? One technique is to check the value of a session variable. The login page, on successful authentication, sets the value of a session variable to True:
Session(“LoginSuccessful”) = True
Each of the subsequent pages checks the value of this session variable and either allows the user to proceed or redirects the user back to the login page:
If Session(“LoginSuccessful”) <> True Then Response.Redirect “login.asp” ‘ – or in Windows 2000, this is better code ‘ – Server.Transfer “login.asp”End if…… The rest of the page’s code here…
This technique relies on session variables, a definite no-no when working with Web server farms where more than one machine can handle requests to the same Web site. In this case, it’s possible that Login.asp may be served by Server 1 and the subsequent calls to page A, B, or C can be handled by another server.
Because session variables are limited to the Web server on which they are created, the other servers will not be able to access the value. In this situation, you can have valid users who have been authenticated already being denied access to pages simply because the session variable value does not exist. Another issue is that the session variable’s value is maintained for about 20 minutes, even after the user closes the browser. So, if the user is checking for some data on your site after logging in (say the user is checking their e-mail) and uses a public Web terminal?a kiosk or a public machine in a library, for example?any user who uses that machine after the legitimate user has closed the browser and walked off can access the data even without logging in. If there are any other browser windows still open, all they have to do is to type in the URL for your pages in the browser, and poof?they get to see the data because the session variable value is still true! Definitely not a good idea!
Another technique is to make sure that select pages never accept requests directly from the browser but always be “referred” from another page within the same site. You can check the value of the page that referred another page by using the HTTP_REFERER server variable. So, in each of the subsequent pages, you can check for the value:
If Request.ServerVariables(“HTTP_REFERER”) <> SomeValidValue Then Response.Redirect “login.asp” ‘ – or in Windows 2000, this is better code ‘ – Server.Transfer “login.asp”End if…… The rest of the page’s code here…
The question is: what will you use for the value of “SomeValidValue” in the code above? Should it be “login.asp”? That may not always be possible. How about just checking for the initial X characters and making sure that it equals your Web server or domain name? For example:
If Left(Request.ServerVariables(“HTTP_REFERER”),19) <> “http://www.mydomain” Then
I have found that HTTP_REFERER is not always reliable. Its value depends on the browser type, the means by which the user arrived at the site and probably other factors I don’t know about. I have found it to be very fickle?let me know if your experience has been different.
The Cookie Is the Answer
So, what technique do I use? I use a simple cookie. When the page “login.asp” authenticates the user, it places a small cookie with a pre-determined value on the user’s computer:
Response.Cookie(“SomeCookieName”) = “SomePreDeterminedValue”
Each of the subsequent pages, simply check the value of the cookie. If the cookie value matches what it is expecting, it allows the user to proceed:
If Request.Cookies(“SomeCookieName”) <> “SomePreDeterminedValue” Then Response.Redirect “login.asp” ‘ – or in Windows 2000, this is better code ‘ – Server.Transfer “login.asp”End if…… The rest of the page’s code here…
The key to this technique working is to set the cookie value without specifying an expiration date. This automatically deletes the cookie when the user closes the browser session. So, if the user has been authenticated today and comes back tomorrow, or even 10 minutes after closing the browser, the cookie value will have been deleted and they will be forced to log in again?as they should be. And returning to our earlier scenario, the public library lurker, who is using a machine just vacated by a legitimate user, and is typing in the URL for your secure pages, will be told to log in again.
Proceed with Caution
Use this technique with caution. A cookie is a very public piece of information. Not only can it be viewed on the machine (if you know where to look), it is also visible via the URL?not the one on the browser, but via URL sniffer programs and log analysis programs. I was shocked to find out that a Web log analysis program such as WebTrends could figure out the names and values of cookies by reading the log files. Placing sensitive information (like passwords) within cookies so users don’t have to keep retyping them, makes them very public pieces of information.
A way to temper this drawback is to make your cookie names and values as cryptic as possible. For example, instead of using a cookie called “LoginSuccessful” with a value of “True” to signify successful log in, call your cookie “A8De0Te987E”, with a value of “d9d76eow987” to signify successful log in. The strings do not mean anything, I just randomly typed on my keyboard to get those values. You can use named constants to make your code easier to read and maintain. Instead of writing the two sections of code like this:
‘ – In login.asp’ – Log in has been successful, place cookie nowResponse.Cookies(“LoginSuccessful”) = “True”
‘ – In other pages, check for the cookieIf Request.Cookies(“LoginSuccessful”) <> “True” Then Response.Redirect “login.asp” ‘ – or in Windows 2000, this is better code ‘ – Server.Transfer “login.asp”End if…… The rest of the page’s code here…
change it to this:
‘ – In login.asp’ – Log in has been successful, place cookie nowResponse.Cookies(“A8De0Te987E”) = “d9d76eow987”
‘ – In other pages, check for the cookieIf Request.Cookies(“A8De0Te987E”) <> “d9d76eow987” Then Response.Redirect “login.asp” ‘ – or in Windows 2000, this is better code ‘ – Server.Transfer “login.asp”End if…… The rest of the page’s code here…
To help you maintain your code and make it easier to read, modify the new code above to: Section A:
‘ – In some common include file; so it is visible to all pagesCONST LOGIN_SUCCESSFUL = “A8De0Te987E”CONST LOGIN_SUCCESSFUL_TRUE = d9d76eow987
‘ – In login.asp ‘ – Log in has been successful, place cookie nowResponse.Cookies(LOGIN_SUCCESSFUL)= LOGIN_SUCCESSFUL_TRUE
‘ – In other pages, check for the cookieIf Request. Cookies(LOGIN_SUCCESSFUL)<> LOGIN_SUCCESSFUL_TRUE Then Response.Redirect “login.asp” ‘ – or in Windows 2000, this is better code ‘ – Server.Transfer “login.asp”End if…… The rest of the page’s code here…
You can also make the setting and the checking for the login state modular by placing them within subroutine calls. This allows them to be called from any page that requires them. And instead of maintaining several copies of the same code, you can make function calls and maintain code in a single location. Let’s gather all the necessary code and place it in a new ASP page called “IncLoginStateMechanism.asp”. This is how it will look:
<%' - File: IncLoginStateMechanism.asp' - In some common include file; so it is visible to all pagesCONST LOGIN_SUCCESSFUL = "A8De0Te987E"CONST LOGIN_SUCCESSFUL_TRUE = d9d76eow987Sub SetLoginStateSuccessful()Response.Cookies(LOGIN_SUCCESSFUL)= LOGIN_SUCCESSFUL_TRUEEnd SubSub VerifyLogin()If Request. Cookies(LOGIN_SUCCESSFUL)<> LOGIN_SUCCESSFUL_TRUE Then Response.Redirect “login.asp” ‘ – or in Windows 2000, this is better code ‘ – Server.Transfer “login.asp”End ifEnd Sub%>
All pages can then include the new IncLoginStateMechanism.asp page to be able to use its constants and subroutines. The login page, on authentication, calls the SetLoginStateSuccessful routine
‘ – In login.asp ‘ – Log in has been successful, place cookie nowSetLoginStateSuccessful
All the other pages call the VerifyLogin routine
‘ – In PageA.asp’ – Verify that Log in has been successfulVerifyLogin…… The rest of the page’s code here…
I assume you know how to include the common file within the other pages. You use the directive:
Remember to use the include directive within an HTML portion of the Asp page, and not within the Asp <% and %> tags.
I hope this gives you some ideas about managing login state information. If you have information about other ways you have approached this problem, or about how paranoid you have been in building your applications, drop me a line. I would love to hear from you.