ColdFusion in Context: Release Session Memory

HTTP is stateless. When you close a browser or move to another site, the site you were looking at doesn't usually know. Therefore, if it has reserved memory on the server for your use, that memory is wasted when you leave.

Furthermore, if there's a session in server memory reserved for your use, someone else may be able to hijack the remainder of your session. For the protection of your users and for the overall performance of your site, you should provide a way for them to log out when they're done.

We'll illustrate the problem and look at two ways of handling it. Along the way, we'll work with session variables in ColdFusion. However, similar solutions can be used for other Web middleware.

Define the Session

Create a file named appset.cfm to define the session within an application. Usually, you would put this kind of code in Application.cfm. However, then it would become part of every page in this directory, and that would interfere with the second half of this demonstration.

This code sets the application to expire after ten minutes of inactivity (from any user of the application) and the session to expire after 30 seconds of inactivity (from this user). Whenever a user of the application requests a page, ColdFusion resets both timers. For this demonstration, explicitly avoid using cookies to tie the browser to the session. Using URLs helps show what's going on.

<!--- Simulate Application.cfm --->
<cfapplication name="release"
applicationtimeout="#createTimeSpan(0,0,10,0)#"
sessionmanagement="yes"
sessiontimeout="#createTimeSpan(0,0,0,30)#"
setclientcookies="no">

Open Door Number 1

Create a page - call it door1.cfm - that lets the user change a session variable. Include appset.cfm to define a session when the user requests this page. Remove cookies in case any were set by other applications on this server (to avoid confusion during the demo.) Let the user enter a nickname and then set some session variables.

Numerous authors have written about the importance of locking memory variables before writing to them in a multi-threaded environment such as ColdFusion. Set the lock so no other page can write to variables for this session while you're doing it. You can readily do this by naming your lock after the session ID. (If you're using new versions of ColdFusion, you can replace "name=#session.sessionID#" with "scope=session" to accomplish the same thing.)

If you were adding multiple variables to a cflocation URL, you would have to build the URL with a cfset statement first to make your intent clear. However, you can add a single variable to this URL directly. ColdFusion makes it easy to pass the cfid and cftoken in a URL by providing the special variable URLToken. This variable looks like the cfid and cftoken concatenated for use in a URL. Here's an example: "cfid=83&cftoken=12334556".

<!--- Simulate Application.cfm --->
<cfinclude template="appset.cfm">

<!--- Remove cookies to avoid confusion --->
<cfcookie name="cfid" expires="now">
<cfcookie name="cftoken" expires="now">

<!--- Write to the session structure and go to work --->
<cfif isDefined("form.Nickname")>
  <cflock name="#session.sessionID#" timeout="30">
  <cfset session.ok="1">
  <cfset session.username=#form.Nickname#>
  </cflock>
  <cflocation url="work1.cfm?#session.URLToken#">
</cfif> 
<form action="door1.cfm" name="door" method="post">
Enter employee nickname (anything you want):
<input type="text" name="Nickname" value="" size="10" maxlength="8">
<input type="submit" name="submit" value="OK?">
</form>

Set up (Work) Task Number 1

Create a page - call it work1.cfm - where the user can do some work and eventually log out. If the session's no longer in memory, show this user the door. If the user wants to log out, send the user to the first exit. Otherwise, as long as the user presses the Submit button more frequently than every 30 seconds, the session will stay active and the number in the form's text field will keep rising.

<!--- Simulate Application.cfm... --->
<cfinclude template="appset.cfm">

<!--- Bounce the user if the session's over --->
<cfif not isDefined("session.ok")>
  <cflocation url="door1.cfm">
</cfif>

<!--- Permit logout and work --->
<cfif isDefined("form.logout")>
  <cflocation url="exit1.cfm?#session.URLToken#">
</cfif>

You are logged in as <br>
<cfoutput>
#session.username#, session ID #session.cfid#, and token #session.cftoken#<p>
</cfoutput>
Pressing the button simulates work;<br>
staying idle too long logs you out;<br>
clicking "Log Out" also 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">
<input type="submit" name="logout" value="Log Out">
</cfform>

Build Exit Number 1

Make a page named exit1.cfm. It will capture information about the session (to help show what's going on) and will then remove this session from memory.

<!--- Simulate Application.cfm --->
<cfinclude template="appset.cfm">

<!--- Prove that this works --->
<cfset myURLToken=session.URLToken>
Leaving session <cfoutput>#session.cfid#</cfoutput>
<cfset target="work1.cfm?#myURLToken#">
<p>
<cfoutput>
Try to work by clicking here...<a href="#target#">#target#</a>
</cfoutput>

<!--- Make it work --->
<cfset dummy=StructClear(session)>

Build Door Number 2

COPY door1.cfm to door2.cfm, then change its internal references from door1.cfm to door2.cfm and work1.cfm to work2.cfm.

Set Up (Work) Task Number 2

COPY work1.cfm to work2.cfm, then change its internal references from work1.cfm to work2.cfm, door1.cfm to door2.cfm, and exit1.cfm to exit2.cfm.

Build Exit Number 2

If the code in appset.cfm is not in Application.cfm in the same directory as this exit for your application, you can use this alternate method to remove the session: time it out instantly. Call this code exit2.cfm. Simply set the session length to zero and send the user to door number 2.

<cfapplication name="release"
applicationtimeout="#createTimeSpan(0,0,10,0)#"
sessionmanagement="yes"
sessiontimeout="#createTimeSpan(0,0,0,0)#"
setclientcookies="no">

<cflocation url="door2.cfm">

Try Both Doors

Browse door1.cfm and enter any nickname. You'll be led to work1.cfm, where you must press Submit frequently or be kicked out. Open a second browser. It's more dramatic if the browser isn't the same type, but since you're not using cookies, it shouldn't matter. Just after pressing Submit again (so you have nearly 30 seconds to play around), copy the URL into this second browser and go there. You'll see that you can do it readily. Then log out from one browser and try the same trick again from either browser. Notice that it won't work now. The session no longer exists in memory. You're stopped cold.

If you merely press the back button from exit1.cfm, it appears that you have an active session again, but when you press the Submit button, you'll be bounced to door1.cfm. You can get a taste of the same thing by clicking on the link in exit1.cfm. You'll get dumped to door1.cfm.

Now browse door2.cfm and enter a nickname as you did for door1.cfm. You'll see the same behavior. Try opening a second browser as before, but this time, close the first browser or send it to a different site before sending the second browser to the URL you've copied. It doesn't make any difference. The site didn't know that the first browser had left; so, it left the session active on the server. Now log out. As soon as you do, you'll be bounced to door2.cfm.

Recap

This isn't the final answer. It relies on the user to take explicit action or waits for a timeout. Because it doesn't externally tie the session to something external that interferes with misuse such as a specific time, IP, or user, the session identification is less secure than it could be. However, it should improve your server's performance by reducing the maximum amount of memory used by ColdFusion for session variables. It should also improve user security by removing unused sessions from the server. =Marty=

[After publication, the phrase "or waits for a timeout" was added to the Recap for clarification.]