Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with:

Explicitly Ending A ColdFusion Session

Posted by Ben Nadel
Tags: ColdFusion

Yesterday's post on the misconceptions behind clearing the ColdFusion Session scope sparked some really great conversation in the comments. Not only did I learn a thing or two, it got me thinking about the ways in which we can actually end a user's ColdFusion session. As a follow-up post, I thought it would be good to explore a few different approaches to ColdFusion session termination. All of these solutions revolve around dynamically changing a given user's session timeout; but each differs in terms of its organization and use of documented vs. undocumented features.

Approach One: Changing The Session Timeout In The Application.cfc Pseudo Constructor

As has been demonstrated before, the Application.cfc ColdFusion framework component is instantiated for every single page request in a given application. This gives us the ability to, on every single page request, dynamically change the current settings for both the application as well as for the session associated with the given request. In this approach, we're going to listen for a URL flag from within the Application.cfc pseudo constructor; and, if detected, we're going to kill the user's session timeout for the given request.

 
 
 
 
 
 
 
 
 
 

Killing the user's session timeout is not sufficient to end the user's session; at least, not all the time. Once we kill the session timeout, we also have to clear the user's session cookies. This way, any redirects or further navigation taken by the user will prevent the ColdFusion application from persisting the current session. Remember, the timeout settings are based purely on inactivity across page requests. That said, let's take a look at the Application.cfc in approach one. Take note of both the pseudo constructor and the onRequestStart() event handler:

Application.cfc

  • <cfcomponent
  • output="false"
  • hint="I define application settings and event handlers.">
  •  
  • <!--- Define the application settings. --->
  • <cfset this.name = hash( getCurrentTemplatePath() ) />
  • <cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) />
  • <cfset this.sessionManagement = true />
  • <cfset this.sessionTimeout = createTimeSpan( 0, 0, 3, 0 ) />
  •  
  •  
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  •  
  •  
  • <!---
  • Check to see if we are supposed to kill the user's
  • currently active session.
  • --->
  • <cfif structKeyExists( url, "killSession" )>
  •  
  • <!---
  • Override the session timeout so that it will timeout
  • immediately (approximately - it might be slightly
  • delayed).
  •  
  • NOTE: Using a ZERO timeout seems to be ignored by
  • the ColdFusion framework. Hence, I am using 1 second.
  • --->
  • <cfset this.sessionTimeout = createTimeSpan( 0, 0, 0, 1 ) />
  •  
  • <!---
  • NOTE: We have to get OUT of the pseudo constructor
  • before we redirect the user otherwise the new
  • sessionTimeout value will not actually stick.
  •  
  • Also, we CANNOT delete the cookies in the pseudo
  • constructor, otherwise new cookies will be assigned
  • to the tiny timeout and we won't *actually* timeout
  • the current session.
  • --->
  •  
  • </cfif>
  •  
  •  
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  •  
  •  
  • <!--- Define page request settings. --->
  • <cfsetting
  • requesttimeout="10"
  • showdebugoutput="false"
  • />
  •  
  •  
  • <cffunction
  • name="onSessionStart"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I initialize the session.">
  •  
  • <!---
  • Set up a hit count variable so that we can see
  • how many page requests are recorded in this user's
  • session.
  • --->
  • <cfset session.hitCount = 0 />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I initialize the page request.">
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!--- --------------------------------------------- --->
  • <!--- --------------------------------------------- --->
  •  
  • <!---
  • Check to see if we killed the session timeout in the
  • psuedo constructor. If we did, we can / should now
  • kill the cookies for the current session and then
  • redirect such that the user can get their new session.
  • --->
  • <cfif structKeyExists( url, "killSession" )>
  •  
  • <!---
  • Clear all of the session cookies. This will
  • expire them on the user's computer when the
  • CFLocation executes.
  • --->
  • <cfloop
  • index="local.cookieName"
  • list="cfid,cftoken,cfmagic">
  •  
  • <!--- Expire this session cookie. --->
  • <cfcookie
  • name="#local.cookieName#"
  • value=""
  • expires="now"
  • />
  •  
  • </cfloop>
  •  
  • <!---
  • Redirect back to the primary page (so that we dont
  • have the killSession URL parameter visible).
  • --->
  • <cflocation
  • url="./index.cfm"
  • addtoken="false"
  • />
  •  
  • </cfif>
  •  
  • <!--- --------------------------------------------- --->
  • <!--- --------------------------------------------- --->
  •  
  •  
  • <!--- Increment hit count. --->
  • <cfset session.hitCount++ />
  •  
  • <!--- Return true so the page can process. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onSessionEnd"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I handle any end-of-session logic.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="sessionScope"
  • type="any"
  • required="true"
  • hint="I am the session scope that is ending."
  • />
  •  
  • <cfargument
  • name="applicationScope"
  • type="any"
  • required="true"
  • hint="I am the application scope parent to the given session."
  • />
  •  
  • <!--- Output the CFID and CFTOKEN values to the log. --->
  • <cffile
  • action="append"
  • file="#getDirectoryFromPath( getCurrentTemplatePath() )#log.cfm"
  • output="ENDED: #arguments.sessionScope.cfid#<br />"
  • />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

In this Application.cfc ColdFusion framework component, notice that we are looking for the URL flag, killSession. If detected, we are acting on it in two ways: we kill the session timeout within the pseudo constructor; and, we clear the user session cookies within the onRequestStart() event handler. For the session timeout, I went with one second. The ColdFusion documentation indicates that a timeout of zero seconds can be used; but in my experience, a zero second timeout appears to be ignored.

As I stated above, clearing the user's session cookies is an essential part of this process. If we had killed the session timeout and then immediately redirected the user back to the primary display page (so as to get rid of the URL flag), chances are, ColdFusion will reassociate the second request with the previous session before it has had a chance to timeout. So, in essence, we are actually creating a new session so as to allow the previous session to terminate without disruption (which happens within a few seconds - it's not an exact science).

To test this approach, I made use of the onSessionEnd() event handler which writes the ending session's CFID to a log file. This log file then gets included and displayed in my primary display page:

Index.cfm

  • <cfoutput>
  •  
  • <h1>
  • Explicitly Ending ColdFusion Sessions
  • </h1>
  •  
  • <p>
  • Hit Count:
  • #session.hitCount#
  • ( <a href="index.cfm">Refresh</a> )
  • </p>
  •  
  • <p>
  • CFID: #session.cfid#<br />
  • CFTOKEN: #session.cftoken#<br />
  • </p>
  •  
  • <p>
  • <a href="index.cfm?killSession">End Session</a>
  • </p>
  •  
  • <!--- Include the log file (for sesion ending). --->
  • <cfinclude template="log.cfm" />
  •  
  • </cfoutput>

To see the output generated by this page, check out the video above. Due to the time-sensitive behavior of this exploration, it is not something that I can easily present in a graphic.

Approach Two: Changing The Session Timeout Using The Session Scope's Undocumented Features

In the comments yesterday, David Boyer pointed out that the ColdFusion Session scope has an undocumented method, Session.setMaxInactiveInterval( long ). This method, as you can probably gather from the name, sets the maximum amount of time that a session can be inactive (ie. the user has not made any subsequent page requests). In other words, it sets the session timeout for the current user. And, because it is a method on the Session scope, it means that we can perform this action where ever and whenever we have access to the Session.

 
 
 
 
 
 
 
 
 
 

Using this undocumented method, we can now refactor our Application.cfc to kill the user's session without having to deal with the pseudo constructor:

Application.cfc

  • <cfcomponent
  • output="false"
  • hint="I define application settings and event handlers.">
  •  
  • <!--- Define the application settings. --->
  • <cfset this.name = hash( getCurrentTemplatePath() ) />
  • <cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) />
  • <cfset this.sessionManagement = true />
  • <cfset this.sessionTimeout = createTimeSpan( 0, 0, 3, 0 ) />
  •  
  • <!--- Define page request settings. --->
  • <cfsetting
  • requesttimeout="10"
  • showdebugoutput="false"
  • />
  •  
  •  
  • <cffunction
  • name="onSessionStart"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I initialize the session.">
  •  
  • <!---
  • Set up a hit count variable so that we can see
  • how many page requests are recorded in this user's
  • session.
  • --->
  • <cfset session.hitCount = 0 />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I initialize the page request.">
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!--- --------------------------------------------- --->
  • <!--- --------------------------------------------- --->
  •  
  • <!---
  • Check to see if we are supposed to kill the user's
  • currently active session.
  • --->
  • <cfif structKeyExists( url, "killSession" )>
  •  
  • <!---
  • Change the timeout on the current session scope.
  • This is an undocumented function. We are changing
  • it to one second.
  • --->
  • <cfset session.setMaxInactiveInterval(
  • javaCast( "long", 1 )
  • ) />
  •  
  • <!---
  • Clear all of the session cookies. This will
  • expire them on the user's computer when the
  • CFLocation executes.
  •  
  • NOTE: We need to do this so that the redirect
  • doesn't immediately pick up the previous session
  • within the new, one-second timeout (which would
  • completely defeat our purpose).
  • --->
  • <cfloop
  • index="local.cookieName"
  • list="cfid,cftoken,cfmagic">
  •  
  • <!--- Expire this session cookie. --->
  • <cfcookie
  • name="#local.cookieName#"
  • value=""
  • expires="now"
  • />
  •  
  • </cfloop>
  •  
  • <!---
  • Redirect back to the primary page (so that we dont
  • have the killSession URL parameter visible).
  • --->
  • <cflocation
  • url="./index.cfm"
  • addtoken="false"
  • />
  •  
  • </cfif>
  •  
  • <!--- --------------------------------------------- --->
  • <!--- --------------------------------------------- --->
  •  
  •  
  • <!--- Increment hit count. --->
  • <cfset session.hitCount++ />
  •  
  • <!--- Return true so the page can process. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onSessionEnd"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I handle any end-of-session logic.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="sessionScope"
  • type="any"
  • required="true"
  • hint="I am the session scope that is ending."
  • />
  •  
  • <cfargument
  • name="applicationScope"
  • type="any"
  • required="true"
  • hint="I am the application scope parent to the given session."
  • />
  •  
  • <!--- Output the CFID and CFTOKEN values to the log. --->
  • <cffile
  • action="append"
  • file="#getDirectoryFromPath( getCurrentTemplatePath() )#log.cfm"
  • output="ENDED: #arguments.sessionScope.cfid#<br />"
  • />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

Notice in this approach that our pseudo constructor simply takes care of defining all of the default application settings; the meat of the action takes place entirely within the onRequestStart() event handler. This time, upon detection of the killSession URL flag, we set the session timeout to one second using the setMaxInactiveInterval() method, clear the session cookies, and redirect the user. The mechanism for killing the session in this approach is exactly the same as in the previous approach; the only difference is in how we clear the session timeout.

The primary display page, index.cfm, is exactly the same in this approach so I won't bother showing it. And again, to see the output generated by this page, check out the video above. Due to the time-sensitive behavior of this exploration, it is not something that I can easily present in a graphic.

Approach Three: Changing The Session Timeout In A Sub-Application

Both of the previous approaches work well; but at some level, I feel that they dilute the primary purpose of the Application.cfc ColdFusion framework component. Of course, that could be a completely emotional reaction seeing as this activity - killing sessions - is very much related to the framework itself. Regardless, I thought it would be interested to see if we could take the session termination and factor it out into a sub-application.

 
 
 
 
 
 
 
 
 
 

In this approach, we are going to move all of the session timeout logic into an Application.cfc contained within a sub folder of our main application. In this way, we can leave our primary Application.cfc seemingly more pristine. Here is our main Application.cfc:

Application.cfc (main)

  • <cfcomponent
  • output="false"
  • hint="I define application settings and event handlers.">
  •  
  • <!--- Define the application settings. --->
  • <cfset this.name = hash( getCurrentTemplatePath() ) />
  • <cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) />
  • <cfset this.sessionManagement = true />
  • <cfset this.sessionTimeout = createTimeSpan( 0, 0, 3, 0 ) />
  •  
  • <!--- Define page request settings. --->
  • <cfsetting
  • requesttimeout="10"
  • showdebugoutput="false"
  • />
  •  
  •  
  • <cffunction
  • name="onSessionStart"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I initialize the session.">
  •  
  • <!---
  • Set up a hit count variable so that we can see
  • how many page requests are recorded in this user's
  • session.
  • --->
  • <cfset session.hitCount = 0 />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I initialize the page request.">
  •  
  • <!--- Increment hit count. --->
  • <cfset session.hitCount++ />
  •  
  • <!--- Return true so the page can process. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="onSessionEnd"
  • access="public"
  • returntype="void"
  • output="false"
  • hint="I handle any end-of-session logic.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="sessionScope"
  • type="any"
  • required="true"
  • hint="I am the session scope that is ending."
  • />
  •  
  • <cfargument
  • name="applicationScope"
  • type="any"
  • required="true"
  • hint="I am the application scope parent to the given session."
  • />
  •  
  • <!--- Output the CFID and CFTOKEN values to the log. --->
  • <cffile
  • action="append"
  • file="#getDirectoryFromPath( getCurrentTemplatePath() )#log.cfm"
  • output="ENDED: #arguments.sessionScope.cfid#<br />"
  • />
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, this component simply defines the default application and session settings; there is nothing here about ending sessions or clearing cookies. The display page for this approach is very slightly different; rather than using a link containing a killSession URL flag, our "End Session" link directs the user to the sub application:

Index.cfm

  • <cfoutput>
  •  
  • <h1>
  • Explicitly Ending ColdFusion Sessions
  • </h1>
  •  
  • <p>
  • Hit Count:
  • #session.hitCount#
  • ( <a href="index.cfm">Refresh</a> )
  • </p>
  •  
  • <p>
  • CFID: #session.cfid#<br />
  • CFTOKEN: #session.cftoken#<br />
  • </p>
  •  
  • <p>
  • <a href="./kill/index.cfm">End Session</a>
  • </p>
  •  
  • <!--- Include the log file (for sesion ending). --->
  • <cfinclude template="log.cfm" />
  •  
  • </cfoutput>

Notice that this time, the "End Session" link directs the user to the URL, "./kill/index.cfm". This "kill" sub-folder contains its own Application.cfc ColdFusion framework component which is responsible for ending the user's current session:

Application.cfc (sub-directory)

  • <cfcomponent
  • output="false"
  • hint="I exist only to extend the base application and kill the session.">
  •  
  • <!---
  • Set the name of the application. It is critical that this
  • application have the same NAME as the root application
  • (in order to override the settings). However, since we
  • have hashed the application based on the root Appliation
  • file name, we have to use the same file name... which is
  • the current Application.cfc *minus* the current directory.
  • --->
  • <cfset this.name = hash(
  • reReplaceNoCase(
  • getCurrentTemplatePath(),
  • "[\\/][^\\/]+([\\/]Application\.cfc)$",
  • "\1"
  • )
  • ) />
  •  
  • <!---
  • Define the application session settings. Notice that we
  • are using a one second timeout for the session.
  • --->
  • <cfset this.sessionManagement = true />
  • <cfset this.sessionTimeout = createTimeSpan( 0, 0, 0, 1 ) />
  •  
  •  
  • <cffunction
  • name="onRequestStart"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I initialize the page request; but really, I am only here to kill the user cookies and redirect back to the root application.">
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!---
  • Clear all of the session cookies. This will expire
  • them on the user's computer when the CFLocation
  • executes.
  • --->
  • <cfloop
  • index="local.cookieName"
  • list="cfid,cftoken,cfmagic">
  •  
  • <!--- Expire this session cookie. --->
  • <cfcookie
  • name="#local.cookieName#"
  • value=""
  • expires="now"
  • />
  •  
  • </cfloop>
  •  
  • <!---
  • Redirect back to the primary page of the root
  • application.
  • --->
  • <cflocation
  • url="../index.cfm"
  • addtoken="false"
  • />
  •  
  • <!--- Return true so the page can process. --->
  • <cfreturn true />
  • </cffunction>
  •  
  • </cfcomponent>

You'll notice here that the only responsibility of this sub-Application.cfc is to kill the user's session. Now, this Application.cfc is not actually extending the base Application.cfc (that's such a pain in the paths); as such, the key part of this approach is that this application has the same Name as the root application. That's why I am removing the current directory from the getCurrentTemplatePath() value before I hash it and use it to define "this.name". By giving both Application.cfc instances the same name, the ColdFusion application server will use both of them to interact with the same application.

Because this Application.cfc has a single responsibility, I don't have to check for any URL flags; I simply set the session timeout to one second within the pseudo constructor and then clear the session cookies in the onRequestStart() event handler. Once the cookies have been cleared, I then redirect the user back to the root application. I like this approach because I think it cleans up the Application.cfc a bit; but, there is definitely some unease that I feel about having two Application.cfc definitions feeding into the same application.

In yesterday's post and comments, it was mentioned that clearing a session scope has some benefits related to garbage collection as well as security; in these demos, the only reason that I have chosen not to clear the session scope is because I am using the CFID in the onSessionEnd() application event handler. Had I not been using it, clearing the session would have been fine.

Because ColdFusion applications revolve around memory allocation more so than on "running" applications in the traditional sense, killing an application or a session is not something that lends particularly well to the ColdFusion framework. By tweaking session timeouts on a per-request basis, however, we can essentially kill ColdFusion sessions by asking them to terminate much sooner than originally intended. While all of the three approaches explored in this post are different, they all spring from this one central idea. I hope this exploration has perhaps shed some light on the lifecycle of the ColdFusion framework and how we can leverage it to, more or less, actively terminate user sessions.

Tweet This Interesting post by @BenNadel - Explicitly Ending A ColdFusion Session Thanks my man — you rock the party that rocks the body!



Reader Comments

Very information post. I agree with you about always being aware about the use of undocumented features. I think it's always a good idea to have alternatives ready in case Adobe take them away.

As soon as the CF10 prerelease program starts, I'll be putting my case forward for making setMaxInactiveInterval official. It has been around since CF7 so why not? ;)

Reply to this Comment

@David,

Once have things encapsulated, or at least in one area, I don't think it matters which approach you take. Even if things were to drastically change, it would be quite easy to update.

@Eric,

With bots, the cookies are returned; however, the bots are never going to post them back to the server... so they might as well not be set at all. As far as how I would handle bots in general, I might try setting a low session timeout for everyone to begin with and then maybe only give a full session when a user logs in:

http://www.bennadel.com/blog/1831-Delaying-ColdFusion-Session-Persistence-Until-User-Logs-In.htm

It really depends on what the situation is; some public sites might be fine operating simply on cookies alone without session management.

@David,

Ha ha ha, classic.

Reply to this Comment

In method #3, you could also just extend the root Application.cfc, that might make the code a little more intuitive and would allow you to leverage other logic in the root Application.cfc.

(NOTE: This is one of the places where I wish you could reference an object by a relative path.)

Reply to this Comment

@Dan,

Re: relative paths, that's exactly why I didn't do that. Admittedly, it would be a *much* cleaner approach :( Unfortunately, my testing folder is like 8 folders deep from my web root, so I didn't want to make those paths.

Relative pathing would really be the cat's pajamas!

Reply to this Comment

For a simple login system for a webshop do you need to go these lengths when a user logs out using structClear? I cannot do this in the Application.cfc, or MUST I do this in the Appication.cfc instead of the act_logout.cfm page?

I cannot wrap my head around this right now, but maybe you can point me in the right direction?

Reply to this Comment

@Sebastiaan,

I have never personally had to explicitly end a ColdFusion session; typically, I will just throttle the session timeouts at the start; but even that I am finding less necessary.

As far as where you can do it, you can use approach #2 in the logout page since:

session.setMaxInactiveInterval()

... should be accessible from anywhere in your request script processing.

Reply to this Comment

@Ben Nadel,

would this work on Railo as well?

Am currently doing lots of development with Railo Barry on Jetty and Tomcat, with Apache, MySql and Ubuntu.

Reply to this Comment

@Sebastiaan,

As @David said, I don't know if the Java-type stuff will work since Railo's underlying implementation is very different from Adobe's (from what I am told). Setting the session timeout stuff in the pseudo constructor of the Application.cfc, however, should be a core feature of ColdFusion as far as I know.

Reply to this Comment

@Ben,

I am new to CF programming, what is difference between relative and absolute path?

Reply to this Comment

@Siva,

A relative path means that the path is defined relative to the current template (typically using ./ and ../ notation). An absolute path is a path that is defined from the root of the serve or context (as in /some/path/ or c:\some\path).

I think you can also mix and match a bit too; you can add relative paths to absolute paths:

c:\foo\bar\../../foo\bar

Does that help at all?

Reply to this Comment

Nice stuff Ben.

I've got a related problem, well to do with sessions anyway. I'm trying to remove a particular users session based upon the actions of a super-user. ie. be able to kick someone off the site by removing their session, but not the session of the user doing the kicking!

Make sense.

I'm thinking of keeping a list of the active sessions in the server/application scope and then just doing a StructClear() on that particular session.

Any advice/tip/hints/tricks on that at all?

Apologies if you already have a post on this somewhere.

CK

Reply to this Comment

Hi Ben -

I have a question about scenario 3.

Maybe there is some subtle rule that I am missing, so I looking for some guidance.

In the main App.cfc, you create a name by using a hash of the path.

In the sub App.cfc you create a name by using a hash of the main App.cfc path.

Why couldn't you have just used:

cfset this.Name = "test"

for both App.cfc's?

Thanks,

Doug

Reply to this Comment

@Chris,

There's no easy way to kill another users session. The best method be to have an application scope structure like application.sessions[session.sessionId] = userId (from your user database). On every request you'd have to check that the session key exists. When you want to kick someone off, remove their sessionId from that structure.

Another way is to use something like my CfTracker project. It uses a whole bunch of undocumented stuff to give lists of sessions and allow you to stop them, as well as a few other tricks. But keep in mind that there's no future compatibility promise on something like that, as Adobe could easily change things ;)

@Ben + anyone else,

I forgot to mention that I followed this post up with my own investigation into the undocumented side of ColdFusion. I found there's a perfect method for stopping sessions, which also works around problems with setting the timeout to 1 second. Plus I've some hope that it'll make it into CF10 since Adobe added ApplicationStop to CF9.

http://misterdai.wordpress.com/2010/06/15/cf-sessionstop-ending-a-cf-session/

Reply to this Comment

Ben,

This is great post.

I am good with invalidating the sessions, but I am not seeing the session really being cleared.

If I run this script from the servermonitoring cfc of the adminapi, I am not seeing the sessions disappear as you would think.

<cfobject component="cfide.adminapi.administrator" name="admin">
<cfobject component="cfide.adminapi.servermonitoring" name="sm">
<cfset admin.login(YOUR ADMIN PASSWORD)>
<cfoutput>
Active Session: #sm.getActiveSessionCount()#
<hr />
Listing of all Sessions<br />
<cfdump var="#sm.getAllActiveSessions()#">

</cfoutput>

Reply to this Comment

@Mike,

Hmmm, there might be a delay between when the session times out and when the memory space is actually freed? I have never used the API to get that kind of insight. Perhaps Mr. Boyer can offer some insight to that?? It should be removed shortly, assuming the timeout was created and the cookies were erased.

@Doug,

I use the hash() approach just as an easy way to make sure no applications ever get the same name (I don't typically use sub-Application.cfc instances). So, yes, in the Extends="", it would have been much easier to use an app name. Sometimes, I get a little bit of tunnel vision.

@Dave,

Thanks for helping out!

Reply to this Comment

Thanks Ben.

Interested to know if anyone actually sees sessions get cleared (using adminapi) early vs just being orphaned until the session timeout kicks in. I

Reply to this Comment

@Mike,

As far as I know from my digging into sessions... When a session has timed out, it'll continue to exist for certain amount of time but marked as expired. It'll sit there until ColdFusion does a sweep of the sessions and destroys all the expired ones. The delay isn't usually that long, under a minute I think, but might be configurable in some XML somewhere.

If you're still seeing the session exist for longer than expected, the timeout value might not be what you expected. You might want to take a look at my CFTracker tool that provides some useful metadata on each session (unless you're using J2EE sessions).

Also, CF7 and CF8 (possibly, depending on patch level) allow an expired session to be reused if accessed before the expired session sweep takes place. So it's always an idea to clear the users cookies to prevent access as well.

Reply to this Comment

"... For the session timeout, I went with one second. The ColdFusion documentation indicates that a timeout of zero seconds can be used; but in my experience, a zero second timeout appears to be ignored. ..."

I used a Java bytecode viewer (jclasslib bytecode viewer), popped open the cfusion.jar file that resides in the {ColdFusion root}/lib directory, and browsed to the coldfusion.runtime.SessionScope class. The reason why values of zero are ignored is because the SetMaxInactiveInterval method multiplies the incoming argument by 1000 before storing it in the class's private mMaxInactiveInterval field. So it looks like the CF Session handling subsystem treats this value as milliseconds.

Reply to this Comment

@Graeme,

That would make sense. It seems that computer like milliseconds and humans like seconds (understandably -- how often do we write that exact code to multiple by 1000).

@Rose,

Woohoo! Thanks :D

Reply to this Comment

Hi Ben,

Great write-up! I know this post is a little dated by now, but I was testing out your "Approach One" and had a question or two for you.

I'm not sure how ACF handles this, but in Railo (which I use exclusively), any time the session scope is fired up, it immediately sets the CFID cookie with an expiration somewhere in the year 2042. This never changes throughout the users session.

By adding the following code in the Application.cfc file (just below where you define the application settings), I'm able to keep the CFID cookie's expiration date in sync with the session.

  • <cfif isdefined("cookie.cfid")>
  • <cfcookie name="cfid" value="#cookie.cfid#" expires="#this.sessionTimeout#">
  • </cfif>

I'm wondering if this is a good idea or not. My thought is that if someone is in the middle of a session and then heads to lunch, there is no mechanism to clear the browser side session...since the user isn't "logging out", which fires off the ?killsession variable in the url.

Obviously the server side session would timeout normally and then a refresh of the page should cause the CFID cookie to be rebuilt with a new session. However, if your thoughts are that it's always best to get rid of the cookie at the end of a session, then I'm wondering if my approach is a viable option...and any impacts that this constant refresh of the cookie might have on server performance.

Thanks for your time Ben...I really enjoy reading your posts!

Reply to this Comment

I never dealt with Application.cfc before because my main application's code base is 10 years old now! (That's what happens when you're the only Geek on staff!) I'm launching a separate project and need some of the modern features that the CFC approach provides. As usual, a couple of articles from Ben really put me on the right path! Watch out now!

Reply to this Comment

I've been using this technique for a while, but recently I've started playing around with CF10 and it seems that you can no longer manipulate the CFID and CFTOKEN cookies!
On the face of it, this is probably a good security measure, but in this instance it seems to break the best way I know to end a session.
Does anyone know of a way around this?

Reply to this Comment

Ahh. Yes. sessionInvalidate()
RTFM, right? ;)

Wasn't easy to find through Google, but yes, seems to work well for cookie sessions.
Sorry to bother you, carry on.

Reply to this Comment

I have used the second technique of setting the max inactive timer. In CF9, I would set a low sessionTimeout and then increase the inactive timer when the person was legitimately logged in. This allowed sessions to quickly die when no longer needed. This doesn't seem to work in CF10. Has the CF10 behavior changed in this regard?

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.