This tip will explore each of these situations. Along the way, you'll work with session and client variables.
People juggle multiple tasks. Surprised? Regardless of whether you provide links to external sites as part of your site, users may click on a link in E-mail which seizes control of the last browser window opened, and that browser window may be pointing at your site. Thus, it may be perfectly valid for someone to leave your site momentarily and then hit the back button to return. It may be perfectly valid for a user to stop browsing long enough to take a phone message, write a report, look up something on Yahoo!, or go to the bathroom. It may even be valid for the user to have multiple windows open (or even multiple browsers) pointing to various pages of your application.
The extent to which you want to support these kinds of behavior is a tradeoff between convenience and security. You can't tell if the user has left the machine or is just doing something else in front of it (unless Big Brother is watching). You usually can't even tell if the user's browser is still pointed at your site or has moved to another one.
The working definition of a Web session usually revolves around idle time. If a user hasn't asked you for a page in a certain length of time, you assume the session is over and act accordingly. However, some sites may actually dump you off after a length of time even if you are still moving within the site.
The good news is that ColdFusion will automatically remove a session's variables from memory if the client lets enough time go by between page requests. (If the client makes a page request within the time limit, the timer starts over again.)
So how long is long enough? The default is twenty minutes. A different default may be set through ColdFusion Administrator. Your cfapplication tag can set the session timeout to any length that doesn't exceed the maximum set through ColdFusion Administrator and the timeout for the overall application.
The bad news is that someone else can use that time to continue the session without logging in. If the client's machine is available with the browser window still open, then the browser is ready for immediate use (after pressing the back button if the client has left the site).
If you're tracking sessions with URLs, then if someone else was watching and has written the token/id combination down, then that person doesn't even need the client's machine. It doesn't take much knowledge to add variables to the end of the URL in a browser window and jump in when the client walks away from an active session.
Is your client safe from attacks from a co-worker's machine if you used cookies? Not entirely. The default methods of using cookies for session and client management create cookies that remain on the client's disk drive when the browser is closed. Even if you turned off this default and put the id/token combination in temporary cookies only stored in memory, if you're running WebTrends or a similar piece of software, you'll find that it places a permanent cookie containing this combination on the client's machine. Anyone with a moment's access to the client's machine can copy the cookie file and (with a little knowledge) prepare his machine to take over at the right moment. Need I say more?
If you're running a clustered site, then you're probably using client variables in a database instead of session variables in memory. Client variables will remain in the database for days of inactivity rather than hours - the default is 90 days in the registry or 10 in a database - unless you do something explicitly about them. Further, this limit is global, not limited by application. (Client variables are really meant to track clients, not sessions. It takes a bit of work to use them for session management.)
The include file, appset1.cfm, defines an application named time1. Its application variables remain active for 10 minutes; its session variables time out after 20 seconds of inactivity (to make this demo go faster). The default would be to track the session using cookies; we override that here. This kind of code would usually be in Application.cfm, but we're going to vary these parameters later.
<cfapplication name="fred" applicationtimeout="#createTimeSpan(0,0,10,0)#" sessionmanagement="yes" sessiontimeout="#createTimeSpan(0,0,0,20)#" setclientcookies="no">
Here is a trivial login page, door1.cfm, that includes appset1.cfm, goes to work1.cfm or complains depending on what the user entered, and provides a place for the login entry. It deletes cookies only for this example so that you won't see confusing results if cookies accidentally get set.
<cfinclude template="appset1.cfm">
<cfcookie name="cfid" expires="now">
<cfcookie name="cftoken" expires="now">
<cfif isDefined("form.Nickname")>
<cfif Nickname is "Frank">
<cfset session.ok="1">
<cfset session.userid="7">
<cfset work="work1.cfm?cfid=#session.cfid#&cftoken=#session.cftoken#">
<cflocation url="#work#">
<cfelse>
Wrong nickname: use "Frank"<br>
</cfif>
</cfif>
<form action="door1.cfm" name="door" method="post">
Enter employee nickname (say "Frank"): 
<input type="text" name="Nickname" value="" size="10" maxlength="8">
<input type="submit" name="submit" value="OK?">
</form>
Here is a trivial work page, work1.cfm. Whenever you push the button, it will increment the number in the form field to simulate work. If you're idle long enough for the session variables to time out, it takes you back to door1.cfm.
<cfinclude template="appset1.cfm">
<cfif not isDefined("session.ok")>
<cflocation url="door1.cfm">
</cfif>
You are logged in as Frank,<br>
<cfoutput>
UserID #session.userid#, Session ID #session.cfid#,
and token #session.cftoken#<p>
</cfoutput>
Pressing the button simulates work;<br>
staying idle too long logs you out.
<cfif not isDefined("form.myNum")>
<cfset hold=0>
<cfelse>
<cfset hold=#form.myNum#+1>
</cfif>
<cfform name="work" action="work1.cfm?cfid=#session.cfid#&cftoken=#session.cftoken#" method="post">
<cfinput type="text" name="myNum" value="#hold#">
<input type="submit" name="bang" value="Submit">
</cfform>
When you go to door1.cfm to try this out, you'll see that the session id and token have nothing to do with the user's logical identity ("Frank"). The session ID is not the user's ID in the system. The session ID gets reused as other individuals come to the Web site. Therefore, if a shoulder surfer doesn't care whose account he takes over, he can simply record what he sees in the URL and keep trying it from time to time until he catches someone for whom this session ID/token combination corresponds to an active session.
Try to hijack this session. While looking at work1.cfm, copy the URL, close the browser, open the browser, copy the URL into it, and try to go there. (You could just open another window, but that's not as dramatic.) If the variables haven't timed out, you can do it, even from a completely different machine. You're in the working area of the application, and you didn't have to log in.
If you're on the client's machine, you don't even have to have written down the id and token; just re-open the browser. The pulldown history for the URL window will get you some likely candidates; the last one is likely to be correct for our example.
The include file, appset2.cfm, defines an application named time2. Its application variables remain active for 10 minutes. It has no session variables. However, it will assign cfid at the client level. The default would be to track the client using cookies; we override that here. This kind of code would usually be in Application.cfm, but we want to be able to run another example in this directory.
<cfapplication name="time2" applicationtimeout="#createTimeSpan(0,0,10,0)#" clientmanagement="yes" setclientcookies="no">
Here is a trivial login page, door2.cfm, that includes appset2.cfm, goes to work2.cfm or complains depending on what the user entered, and provides a place for the login entry. It deletes cookies only for this example so that you won't see confusing results if cookies accidentally get set.
<cfinclude template="appset2.cfm">
<cfcookie name="cfid" expires="now">
<cfcookie name="cftoken" expires="now">
<cfif isDefined("form.Nickname")>
<cfif Nickname is "Frank">
<cfset client.ok="1">
<cfset client.userid="7">
<cfset work="work2.cfm?cfid=#client.cfid#&cftoken=#client.cftoken#">
<cflocation url="#work#">
<cfelse>
Wrong nickname: use "Frank"<br>
</cfif>
</cfif>
<form action="door2.cfm" name="door" method="post">
Enter employee nickname (say "Frank"): 
<input type="text" name="Nickname" value="" size="10" maxlength="8">
<input type="submit" name="submit" value="OK?">
</form>
Here is a trivial work page, work2.cfm, that goes back to door2.cfm if you're idle long enough to exceed your time limit. If the last visit was more than 20 seconds ago, it will delete client.ok. If client.ok or the last visit aren't defined, it will return to door2.cfm. Otherwise, Whenever you push the button, it will increment the number in the form field.
<cfinclude template="appset2.cfm">
<cfif isDefined("client.LastVisit")
and (dateDiff("s", client.LastVisit, now()) gt 20)>
<cfset dummy=deleteClientVariable("ok")>
</cfif>
<cfif (not isDefined("client.ok")) or (not isDefined("client.LastVisit"))>
<cflocation url="door2.cfm">
</cfif>
You are logged in as Frank,<br>
<cfoutput>Userid #client.Userid#, Client ID #client.cfid#,
and token #client.cftoken#<p>
</cfoutput>
Pressing the button simulates work;<br>
staying idle too long logs you out.
<cfif not isDefined("form.myNum")>
<cfset hold=0>
<cfelse>
<cfset hold=#form.myNum#+1>
</cfif>
<cfform name="work" action="work2.cfm?cfid=#client.cfid#&cftoken=#client.cftoken#" method="post">
<cfinput type="text" name="myNum" value="#hold#">
<input type="submit" name="bang" value="Submit">
</cfform>
Go to door2.cfm and try this out. Except that client variables have to be simple - no arrays or structures are allowed, you can create and access them as if they were in memory. This application acts about the same as the one that uses session variables (and has the same weaknesses).
To make it harder to use a client's machine to hijack a "session" the client isn't using, logical sessions should be terminated after a chosen period of inactivity. Whether working with session variables in memory or client variables in a database, ColdFusion gives you tools to easily determine when to end the session and to do it.
The key to ending a session defined by session variables is to force the browser out of the working area of the application when an active session is no longer detected. Testing any session variable for its existence will do.
The key to ending a "session" defined by client variables is to change the variable corresponding to being logged in when browser has been idle beyond a reasonable period of time and to force the browser out of the working area of the application when this happens. Comparing client.LastVisit to now() is the key to this technique. Technically, a visit to ANY application on your server will update client.LastVisit, but that's probably OK from a practical standpoint.
Of course, there's nothing stopping you from rolling your own methods. After logon, you'll need something the client's browser can pass back to you so the client won't have to log on over again as she moves from page to page. You'll need to notice the time of the client's last page request and compare it to the current time (or equivalent). And, you'll have to use this information for "session" management.
By reducing the number of open sessions at any given time, timeouts reduce the likelihood that an intruder will simply stumble onto an active session by using a valid ID/token combination, but it still happens. People have actually wound up inside applications by following URLs captured by search engines. While this is accidental and infrequent, shoulder surfing is not.
If an additional token based on time is added to the combination of variables required to use pages in the working area of your application, the likelihood that someone can simply guess his way inside is greatly lessened. Consider adding an encrypted time token to the variables you use to track sessions of individuals who are logged into your application. Due to its encryption, it would be difficult to spoof. Because this token would be needed for access and could not be reused later, it would greatly strengthen your security. =Marty=
[The timeout has been reduced from the 45 seconds in the original example to 20 seconds to make its action more obvious.]