This article looks at how to secure access to your Web pages, specifically pages that are intended to be displayed in a certain order. The focus is on a Web application, rather than a normal Web site. It assumes cookies will be enabled, so if you can’t guarantee this, then I’m afraid this isn’t for you. (In my case this is a reasonable assumption, because the browser configuration has been agreed to by the customer!)
Another important issue to mention up front is that it is vital to ensure that each page expires immediately. The method used to protect the pages relies on the fact that the ASP is run every time the page is accessed. Without the page expiring, the user can bring up an old version of the page (with the Back button for example) and mess everything up. So if you don’t like network round trips for every page, then this isn’t for you.
The example code that follows is based on a Web application that I have developed, and includes the folder structure and ASP file structure that was implemented. I think it breaks everything up quite nicely, but feel free to use your own preferred structure if you wish. Notice that each screen submits itself to an ASP page with the same name but with Submit on the end. This is to separate the "processing" ASP (i.e., ASP that creates components, access the database, etc.) from the HTML as much as possible.
Getting Started
Let’s start with an example. You have a Customer Registration (CustReg) process that is spread out over three screens (CustReg1.asp, CustReg2.asp, and CustReg3.asp). The problem is that you don’t want the user to be able to jump straight to CustReg3.asp and hit the Create Customer button. So how do we stop them being able to do it?
<%
'----------------------------------------------------------------------------
'Purpose: This file includes headers, constants, and routines that are common
' to all pages in the Web application.
'----------------------------------------------------------------------------
Option Explicit
'Header details (to ensure pages are not cached).
Response.CacheControl = "no-cache"
Response.AddHeader "Pragma", "no-cache"
Response.ExpiresAbsolute = Now()- 1000
Response.Expires = 0
'Cookie constants.
Const gstrCOOKIE_SCREENID = "CookieScreenID"
Sub SetScreenID(strNewScreenID)
'---------------------------------------------------------------------------
'Purpose: To set the current Screen ID.
'Author: EdwardM 9/2/00
'Inputs: strNewScreenID. The ID to indicate the current screen.
'---------------------------------------------------------------------------
Response.Cookies(gstrCOOKIE_SCREENID) = strNewScreenID
End Sub
Function GetScreenID
'---------------------------------------------------------------------------
'Purpose: To get the current Screen ID.
'Author: EdwardM 9/2/00
'Returns: The current ScreenID.
'---------------------------------------------------------------------------
GetScreenID = Request.Cookies(gstrCOOKIE_SCREENID)
End Function
%>
This file includes all code, constants, and routines that are common to all the pages in the Web application. It starts off with Option Explicit (this saves having it in every file) and then ensures the page will expire immediately.
NOTE: From my testing I know that only 1 line of these 4 is required to make my page expire (the first one). But that’s only on this browser with this operating system. The expiry is so vital, however, that I’ve left all the lines in so that it will handle as many browser /operating systems variations as possible.
It then defines constants that will be used to access cookies. In this case there’s only one, and it’s this that will store which screen the user is currently on.
Next come the common routines. There are two routines, one to set the current ScreenID and one to get the current ScreenID. All they do is provide central locations to access the ScreenID in the relevant cookie so that the rest of the app doesn’t actually know where the values are stored (this is just good coding practice).
This file defines constants used to identify each screen. In a real Web app this is going to be a big list because you need a constant for each page.
Custregscreens
Now the CustRegScreens-specific include file.
<!--#include file = "../Includes/CommonCode.asp"> -->
<!--#include file = "../Includes/ScreenIDs.asp"> -->
<%
'Redirection constants.
Const gstrINVALID_CUST_REG_SCREEN_REDIRECT = "CustReg1.asp"
'Common routines to the CustReg process.
Sub CheckCustRegScreenOrder(strCheckingScreen)
'----------------------------------------------------------------------------------
'Purpose: This will determine if the screen specified screen can be accessed from the
' current screen.
'Author: EdwardM 10/2/00
'Inputs: strCheckingScreen, the screen performing the check.
'----------------------------------------------------------------------------------
Dim blnOrderValid
Dim strCurrentScreen
'Assume the screen order will be INVALID.
blnOrderValid = False
strCurrentScreen = GetScreenID
‘If the current screen is invalid, then we will allow this so that the checking screen becomes valid.
If (strCurrentScreen = gstrSCREEN_INVALID) Then
blnOrderValid = True
Else
Select Case strCheckingScreen
Case gstrSCREEN_CUST_REG_1
'We don't care where we came from to get here.
blnOrderValid = True
Case gstrSCREEN_CUST_REG_1_SUBMIT
'We should only be coming here from CustReg1.
If (strCurrentScreen = gstrSCREEN_CUST_REG_1) Then
blnOrderValid = True
End If
Case gstrSCREEN_CUST_REG_2
'We should only be coming here from CustReg1Submit (normal screen flow),
'CustReg2Submit (there was a validation error).
'CustReg3 (the user pressed back),
'or from ourselves (a refresh)
If (strCurrentScreen = gstrSCREEN_CUST_REG_1_SUBMIT) Or _
(strCurrentScreen = gstrSCREEN_CUST_REG_2_SUBMIT) Or _
(strCurrentScreen = gstrSCREEN_CUST_REG_3) Or _
(strCurrentScreen = gstrSCREEN_CUST_REG_2) Then
blnOrderValid = True
End If
Case gstrSCREEN_CUST_REG_2_SUBMIT
'We should only be coming here from CustReg2.
If (strCurrentScreen = gstrSCREEN_CUST_REG_2) Then
blnOrderValid = True
End If
And so on…….
Case Else
'An unknown CustReg screen has been specified.
'Treat this as invalid (take no action).
End Select
End If
If blnOrderValid Then
Call SetScreenID(strCheckingScreen)
Else
Call SetScreenID(gstrSCREEN_INVALID)
Response.Redirect(gstrINVALID_CUST_REG_SCREEN_REDIRECT)
End If
End Sub
%>
This file contains the routine that will actually do the ID checking for the CustReg process. First it needs to include the CommonCode and ScreenIDs files. It then defines a constant that contains the page that will be used if the user tries to access the screens out of sequence (in this case it just goes back to the first screen in the process, but it could just as easily go to an error page).
You may be wondering why ScreenID constants relating to the CustReg process aren’t defined here (as this is the CustReg-specific file). If they were defined here, then this would limit the page security that we can implement.
The routine CheckCustRegScreenOrder is the core of this whole idea. It is this routine that stores the screen access logic. Its reasonably straightforward, so I’ll go over it quickly. It takes a parameter that stores the ScreenID of the screen that requested the check. It then gets the current ScreenID. If the current ScreenID is flagged as invalid, then the order is flagged as correct. (This will allow the redirection after a page has been accessed out of sequence to work by having the page redirected to just overwrite the invalid ScreenID with its own.)
If the ScreenID indicates a normal page, however, a Select Case statement is used to determine if the access is valid. I’ve included a few comments here to explain the access it will allow. (Having separate Submit.asp files does make this a little more complicated.)
If the screen order is valid, then the current ScreenID is updated with the ScreenID of the screen that did the checking, otherwise the ScreenID is flagged as invalid and the page is redirected.
Custreg1
Now that we’ve got all the structural stuff out of the way, we can look at the CustReg pages themselves.
OK. OK. I know this screen doesn’t actually do anything, but that’s not the point. It’s only intended to demonstrate the page security.
The are a few important things here. The page specifies the scripting language it will use. It then includes its own process include file, CustRegScreens.asp. Note that it does not include CommonCode.asp because it doesn’t need to. CustRegScreens.asp takes care of that for us.
All it then does to ensure the page is not being accessed incorrectly is call CheckCustRegScreenOrder, passing its own ScreenID to identify itself as the screen performing the check. As we’ve already seen, if the page is being accessed out of sequence, the CheckCustRegScreenOrder routine will handle any redirection. This was done to cut down on code in this page. One call, one line -- nice and simple.
Custreg1submit
Now that the user has entered the data, we need to perform any required processing.
<%@ Language=VBScript %>
<!--#include file = "../Includes/LogonScreens.asp"> -->
<%
'Check the Screen ID.
Call CheckLogonScreenOrder(gstrSCREEN_CUST_REG_1_SUBMIT)
‘You’re processing here.
If (True) Then
Response.Redirect("CusReg2.asp")
Else
Response.Redirect("CustReg1.asp")
End If
%>
As you can see, this page is structurally very similar to the first. I’ve added a little fudge at the end to simulate what a real page would do. The "True" statement in the If would equate to a business rule, or the result of some processing indicated by a flag (e.g., blnSuccess).
Adding More Screens/Processes
In this example I’ve only shown one process, the Customer Registration process. Adding more processes is easy. Say you now have an Amend Supplier process. It fits in to the Web folder structure like this:
That’s a new folder for all the process screens and one file in the include folder to store the CheckSupplierAmendScreenOrder routine and any other constants you need.
And there you are. The pages we’ve looked at now cannot be accessed out of sequence. The user could browse to one directly, type in the relevant URL, or use a bookmark. It won’t matter. As soon as a page realizes that it’s been accessed out of sequence, it will redirect to first page in the process, or wherever you want.
About the Author
Edward Mason is an independent software consultant working in the United Kingdom. He has been writing custom-built enterprise-scale business solutions for the past four years, primarily using Visual Basic, Microsoft Transaction Server (MTS), SQL, and the Distributed Component Object Model (DCOM). In the last year he has been moving into Windows Distributed interNet Application Architecture (DNA) applications using HTML, ASP, and Remote Data Services (RDS), focusing on Web applications rather than Web sites.
Built around the Microsoft CryptoAPI, AspEncrypt helps you harness all major encryption and hashing algorithms such as DES, Triple-DES, RC2, RC4, RSA, MD5 and SHA1 in just a few lines of code. The component can be used in tandem with AspEmail to send encrypted and signed mail in the industry-standard S/MIME format, or with AspUpload to encrypt files as they are being uploaded. AspEncrypt can also be used to issue and manage X.509 digital certificates.
In many web applications it is desirable for both intranet users and external parties to be able to seamlessly log onto the system. The problem this raises is that it is not easy to allow intranet users to log in via Windows integrated authentication while also allowing external parties to log in to the same application using standard forms authentication. This article will show you one way to achieve the best of both worlds when it comes to authentication. [Read This Article][Top]
In this article, Michele Leroux Bustamante discusses authentication, authorization and role-based security in .NET. Along the way, he provides some best practices for implementing role-based security in some typical .NET application scenarios including rich clients, Web applications, and Web services. [Read This Article][Top]
When implementing custom components that require access to restricted resources, implicit impersonation must be used. Jay Nathan shows how to create a class that makes using .NET Impersonation a snap. [Read This Article][Top]
Learn about the execution process of CLR-based programs and how to protect your applications from being easily disassembled back into source code. [Read This Article][Top]
Businesses that utilize encrypted e-mail may find Secure Multipurpose Internet Mail Extensions (S/MIME) to be somewhat restrictive. This article shows how to use security features in PDF as an alternative to S/MIME. [Read This Article][Top]
Bill Gates, in a recent interview, predicted the end of spam by 2006. One of the methods he mentioned involved a challenge only a real live person could handle. Adnan Masood shows how to use AI and .NET to create a user verification scheme that incorporates similar concepts Gates alluded to.
[Read This Article][Top]
Code Access Security (CAS) is the .NET Framework security model that grants
code permission to resources based on "evidence" pertaining to the
encapsulating assembly. In this article, David Myers examines CAS
and explains different configuration methods. [Read This Article][Top]
Zhenlei Cai combines an open source C++ encryption library with SQL Server
extended stored procedures to create a platform neutral, transparent
encryption solution that resides at the database layer. [Read This Article][Top]
Christopher Spann offers a .NET configuration tip that should help ease system administrators' fears of security compromise and thus assuage growing developer demand for a .NET environment. [Read This Article][Top]
You don't have to be a cryptography expert or spend lots of money on third-party components to secure sensitive data in .NET. In this article, Wayne Plourde shows just how easy it is to encrypt cookie data using encryption classes in the .NET System.Security.Cryptography namespace. [Read This Article][Top]
Mailing List
Want to receive email when the next article is published? Just Click Here to sign up.