Clearing The Session Scope Does Not End Your ColdFusion Session

Posted February 11, 2010 at 10:10 AM by Ben Nadel

Tags: ColdFusion

The ColdFusion application lifecycle can be a tricky beast to understand because it hinges more on memory space association and less so on what you might traditionally consider a "running" application. As such, the concept of actively "ending" or "killing" a ColdFusion application or user session is not something that lends itself well to ColdFusion application framework. ColdFusion 9 introduced the ApplicationStop() function to aid in its new Object-Relational Mapping Hibernate integration; but even with this new function, application and session lifecycle still act completely independently.

 
 
 
 
 
 
 
 
 
 

With all the fuzziness about what it means to be a ColdFusion application, I sometimes see people make the assumption that they can end a user's ColdFusion session simply by clearing the Session scope. Unfortunately, this is not true and will only lead to corrupted sessions. If you've used this approach before and it worked (I know I have), it most likely worked by sheer coincidence; meaning, you probably cleared a business-logic-identifier that signaled to your business logic that the user was no longer "logged in." The user's ColdFusion session, however, carried on quite well regardless.

The problem with clearing the Session scope is that the Session scope is not what relates the user to your ColdFusion application; just the opposite in fact - the Session scope is what is given to the user after that relationship has been established. The entities that relates a user to an active session are the CFID and CFTOKEN values (yes, this is a bit different if you enable J2EE sessions). These values persist as cookies and are passed, by the browser, back to the server with every single page request. It is these cookies that associates a user to a given session, which then in turn, makes available a given Session scope.

To demonstrate this, I have set up a simple ColdFusion application that approaches session destruction in two ways: clearing the Session scope and expiring the cookies. Let's take a quick look at the Application.cfc to see how the application has been defined:

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, 0, 30 ) />
  •  
  • <!--- 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 the session hit count (if it exists). --->
  • <cfif structKeyExists( session, "hitCount" )>
  •  
  • <!--- Increment. --->
  • <cfset session.hitCount++ />
  •  
  • </cfif>
  •  
  • <!--- Return true so the page can process. --->
  • <cfreturn true />
  • </cffunction>
  •  
  • </cfcomponent>

There's nothing special going on here. When the session starts up, I am creating a hitCount variable to record page requests. Then, in the onRequestStart() event handler, I simply increment that hitCount value; of course, since we are experimenting with clearing Session scopes, I am checking to see if that value exists before I increment it.

Now that you see how the application is set up, let's take a look at our main display page:

Index.cfm

  • <cfoutput>
  •  
  • <h1>
  • Clearing The ColdFusion Session Scope
  • </h1>
  •  
  • <p>
  • Hit Count:
  •  
  • <!---
  • Make sure the hitCount exists - if we cleared the
  • session scope, then this will not be there.
  • --->
  • <cfif structKeyExists( session, "hitCount" )>
  • #session.hitCount#
  • <cfelse>
  • <em>N/A</em>
  • </cfif>
  •  
  • ( <a href="index.cfm">refresh</a> )
  • </p>
  •  
  • <p>
  • <a href="clear.cfm">Clear Session</a>
  • &nbsp;|&nbsp;
  • <a href="rebuild.cfm">Rebuild Session</a>
  • &nbsp;|&nbsp;
  • <a href="expire.cfm">Expire Cookies</a>
  • </p>
  •  
  • <!--- Dump out the sesion for debugging. --->
  • <cfdump
  • var="#session#"
  • label="Current Session"
  • />
  •  
  • <br />
  •  
  • <!--- Dump out the cookie for debugging. --->
  • <cfdump
  • var="#cookie#"
  • label="Current Cookies"
  • />
  •  
  • </cfoutput>

On this page, not only are we outputting the hitCount value stored in the session, we are also outputting the Session and Cookie scopes to see what value are available. When we hit this page for the first time, we get the following output - take special note of the where the CFID and CFTOKEN values show up:

 
 
 
 
 
 
Clearing The ColdFusion Session Scope - What Is Actually Happening (Image 1) 
 
 
 

As you can see, the CFID and CFTOKEN values are available in both the Session and Cookie scopes. Don't let that distract you, though - the session-scoped ones are not all that important. Take note that the current CFID value is 17603.

Ok, now, let's try to clear the Session scope by clicking on the "Clear Session" link. That will bring us to this page:

Clear.cfm

  • <!---
  • Clear the session scope. All this does is clear the
  • contents of the session scope; it has no bearing on the
  • person's session from an interaction standpoint (other
  • than you might start breaking code).
  • --->
  • <cfset structClear( session ) />
  •  
  • <!--- Redirect back to index page. --->
  • <cflocation
  • url="index.cfm"
  • addtoken="false"
  • />

As you can see, this page simply clears all variables from the user's associated session object and then relocates them back to the primary display page. Sometimes, the assumption is made that this will "end" a user's session; but, when we get back to the display page, you can see that it does not:

 
 
 
 
 
 
Clearing The ColdFusion Session Scope - What Is Actually Happening (Image 2) 
 
 
 

If the use of structClear() actively ended the user's ColdFusion session, then this subsequent request to the application would have initiated a new session and a new call to the onSessionStart() application event handler. As you can see, however, the Session scope has not been repopulated; the user's previous ColdFusion session is still "running", albeit with a severely crippled Session scope.

As I mentioned before, the user's session is still running because the Session scope is a byproduct of the session and not the mechanism behind it. The drivers of the mechanism are the CFID and CFTOKEN cookies which, as you can see above, are still being passed successfully after the Session scope has been cleared. For fun, we can even try to rebuild our previous Session scope based on these cookies. By clicking on the "Rebuild Session" link, we'll be taken to this page:

Rebuild.cfm

  • <!---
  • Since we completely cleared out our session scope, in order
  • to rebuild it, we pretty much have to go back to the source:
  • our Application.cfc. Let's create an instance of it and use
  • it to re-populate our session with the **programmer** defined
  • session information.
  • --->
  • <cfset app = createObject( "component", "Application" ) />
  •  
  • <!--- Rebuild the programmatic session. --->
  • <cfset app.onSessionStart() />
  •  
  • <!---
  • Using the onSessionStart() event handler will only
  • re-populate the session with programmer-defined information,
  • we have to try to put back in some of the automatic data that
  • the ColdFusion framework would have created during session
  • initialization.
  • --->
  • <cfset session.cfid = cookie.cfid />
  • <cfset session.cftoken = cookie.cftoken />
  • <cfset session.sessionid = "#app.name#_#cookie.cfid#_#cookie.cftoken#" />
  •  
  • <!--- Redirect back to index page. --->
  • <cflocation
  • url="index.cfm"
  • addtoken="false"
  • />

Once we've cleared the Session scope, we can't get all of those original values back; what we can do, however, is put the Session scope back into a non-corrupted state. To do this, we need to do two things: re-initialize the Session scope and re-populate the CFID and CFTOKEN values. As you can see above, in order to re-initialize the Session scope, I am creating an instance of the Application.cfc ColdFusion component and then manually invoking the onSessionStart() class method. Because this method refers to the Session scope directly, it will work on the user's current Session. To get the CFID and CFTOKEN values, all we need to do is copy them from the Cookie scope. The SessionID value is then a combination of the application name, the CFID, and the CFToken.

After we've done this, the user is redirected back to the main display page, which gives the following output

 
 
 
 
 
 
Clearing The ColdFusion Session Scope - What Is Actually Happening (Image 3) 
 
 
 

As you can see, the Session scope has been repopulated. Keep in mind, however, that this does not mean that the user's session has been restarted or that this is a new session in any way. Remember, the ColdFusion session is powered by the relationship established by the cookies being passed back by the browser. If we truly want to "end" the current session (so to speak), we have to stop the browser from reposting those cookies. To do this, we have to expire the current CFID and CFTOKEN cookies. By clicking on the "Expire Cookies" link, we get taken to this page:

Expire.cfm

  • <!---
  • Loop over all the cookies and expire them. In reality, we only
  • need to expire the CFID and CFTOKEN session identifiers, but
  • for our purposes, clearing all the cookies is sufficient.
  • --->
  • <cfloop
  • item="name"
  • collection="#cookie#">
  •  
  • <!---
  • Have them expire immediately. This way, when the
  • response is sent to the browser, all of their cookies
  • will be cleared.
  • --->
  • <cfcookie
  • name="#name#"
  • value=""
  • expires="now"
  • />
  •  
  • </cfloop>
  •  
  • <!--- Redirect back to index page. --->
  • <cflocation
  • url="index.cfm"
  • addtoken="false"
  • />

As you can see here, we are simply looping over all of the current cookies (that ColdFusion knows about) and setting them to expire immediately. When these expiration requests get flushed to the browser with the current response, it will prevent the browser from reposting the CFID and CFTOKEN values. In doing so, on the subsequent request to the main display page, the ColdFusion server will think that the user is a new user and provide them with a new session. This will give us the following output:

 
 
 
 
 
 
Clearing The ColdFusion Session Scope - What Is Actually Happening (Image 4) 
 
 
 

As you can, ColdFusion recognized the request for the display page as coming from a new user and therefore generated new CFID and CFTOKEN values (from 17603 to 17604). While this created a new session, please keep in mind that the previous session is still technically "running;" meaning, it has neither timed out nor has the onSessionEnd() event handler been invoked. The pervious session will only really end once it has been inactive for the SessionTimeout duration as defined by the Application.cfc.

Now, while calling structClear() on the ColdFusion Session scope doesn't end the user's session, it does have a place. If you are going to try and end the user's session prematurely (and I don't mean that negatively), calling structClear() on the Session has a value in that it will trigger garbage collection on the Session-scoped values. So, while the session itself is still active, ColdFusion will be able to free up the memory space more immediately.

It should be noted that all of this pertains to standard ColdFusion session management. If you enable J2EE sessions, there are completely different rules, most of which I cannot speak to from experience.

ColdFusion applications are very interesting at the mechanical level and can be a bit confusing. If you were confused at all before, I hope that this exploration has brought some level of clarity to the matter.




Reader Comments

Feb 11, 2010 at 10:25 AM // reply »
31 Comments

Ben,

I remember seeing some hidden functions that would expire sessions properly.

http://jehiah.cz/archive/extended-operations-on-coldfusion-sessions

Basically either session.setMaxInactiveInterval(1) or session.invalidate()

Haven't tested in CF9 though but it works in CF7/8.


Feb 11, 2010 at 10:30 AM // reply »
67 Comments

Also remember that, depending on the cookie scope, you may have to clear several sets of cookies:

http://rickosborne.org/blog/index.php/2009/01/27/coldfusion-getting-a-fresh-session/

-R


Feb 11, 2010 at 10:30 AM // reply »
11,235 Comments

@David,

The session invalidate stuff looks very interesting. I actually just saw that for the first time the other day. After a little research, it looks like this:

getPageContext().getSession().invalidate()

... only works with J2EE sessions, not traditional ColdFusion sessions:

http://livedocs.adobe.com/coldfusion/8/sharedVars_17.html

Now, ironically, the LiveDocs say that structClear() is typically good enough to clear a session; but this has definitely not been my experience.


Feb 11, 2010 at 10:33 AM // reply »
11,235 Comments

@Rick,

What was CFMAGIC?


Feb 11, 2010 at 10:34 AM // reply »
79 Comments

Ben,

Awesome explanation. From a security stand point, doing both of these, I think , would be a good idea. Clear the previous session scope and then expire the cookies and move on to the next one.

This way, if the previous session (which as you said, does live on until expiration) does become compromised, at least it will be empty and is not being actively used, so is essentially worthless.


Feb 11, 2010 at 10:35 AM // reply »
11,235 Comments

@Jason,

Ah, good point; I had not even considered it from a security point of view.


Feb 11, 2010 at 10:56 AM // reply »
31 Comments

@Ben,

Just checked and .setMaxInactiveInterval(1) works quite well. I've just altered my cftracker riaforge project and added the ability to nuke any session. Great fun! Now to place it on the shared development server in work and watch the other developers scratch their heads when their sessions go missing. Mwuahaha.


Feb 11, 2010 at 10:58 AM // reply »
11,235 Comments

@David,

Ha ha ha, cool, I'll check it out :)


Feb 11, 2010 at 11:03 AM // reply »
31 Comments

@Ben haven't released it yet, having fun with it first ;)


Feb 11, 2010 at 11:28 AM // reply »
31 Comments

@Ben,

Now I have http://wp.me/py3ue-cu >:)

Great fun but I can't think of a way to use this power for good...


Feb 11, 2010 at 11:30 AM // reply »
11,235 Comments

@David,

Ok, now I'll check it out ;)


Feb 11, 2010 at 11:51 AM // reply »
170 Comments

I think this is a bit misleading, because your Expire.cfm isn't really expiring the user's session, it's just forcing the server to re-assign them new cookies, thus creating a *new* session. The old session is still in-memory and technically active, it's just no longer being used.


Feb 11, 2010 at 11:54 AM // reply »
11,235 Comments

@Dan,

I tried to explain that as best as I could in the blog post:

While this created a new session, please keep in mind that the previous session is still technically "running;" meaning, it has neither timed out nor has the onSessionEnd() event handler been invoked.


Feb 11, 2010 at 11:59 AM // reply »
3 Comments

Ben, it turns out this is a problem for other languages, too. I occasionally have to do some PHP development and we ran into the same issue there. I hope that we as a community can develop an acceptable solution on killing authenticated sessions without using cflogin, etc.

Thanks for putting this out in the community. I'm curious what Jason Dean comes up with regarding dealing with sessions and security.

Our company doesn't use the session scope, but uses the client scope and I am thinking we might be able to extend the functions of the database store.


Feb 11, 2010 at 12:06 PM // reply »
33 Comments

BTW: Noticed you were using cflocation with a relative url instead of a absolute url. Although most popular browsers will work with relative urls, the standard is for a absolute url, and some older browsers and other protocol-strict http clients will fail with a relative url, just thought I would point it out.


Feb 11, 2010 at 12:08 PM // reply »
79 Comments

@david,

Have you tested setMaxInactiveInterval(1) with CF Sessions? Because my understanding is that it will only work with JEE sessions.

Jason


Feb 11, 2010 at 12:09 PM // reply »
79 Comments

@ben and @dan

Maybe it would make it more clear to call it expireCookie.cfm. Because that is actually what's happening.


Feb 11, 2010 at 12:10 PM // reply »
11,235 Comments

@Brandon,

It's an interesting problem to be sure. I'll probably do some more exploration of this.

@David,

That's interesting - I have not heard that before (not saying it is wrong). I've always used relative paths. Can you point me to any resources on this?


Feb 11, 2010 at 12:52 PM // reply »
67 Comments

CFMAGIC is ColdFusion's way of reminding itself that it's set domain-level cookies.


Feb 11, 2010 at 12:53 PM // reply »
11,235 Comments

@Rick,

Ahh, that makes sense - it's practically self-documenting ;)


Feb 11, 2010 at 1:50 PM // reply »
33 Comments

If you look up the docs on the location http header(which cflocation is doing) it is specified, the php docs are more explicit about it as well as lots of varies rewrite engine docs. Its one of those things that most browsers are forgiving about so usually it doesn't matter, but it is something to be aware of.

http://www.ietf.org/rfc/rfc2616.txt RFC 2616, section 14.30


Feb 11, 2010 at 2:07 PM // reply »
9 Comments

Does anyone know what CF does internally based on the session expiration time configured either with the admin or at the application.cfc level? Does it garbage collect?


Feb 11, 2010 at 2:16 PM // reply »
1 Comments

Great information and explanation. Thanks Ben.


Feb 11, 2010 at 3:24 PM // reply »
79 Comments

@David

CFTracker is very cool. I just downloaded it and was playing around. You have definitely figured out how to do something that I have been trying to do for a long time. It seems that you can, in fact, expire ColdFusion sessions with setMaxInactiveInterval(1).

That is awesome. Thanks!


Feb 11, 2010 at 4:43 PM // reply »
31 Comments

@Jason,

Glad you like it. It's just a little utility cfc for stats and information, but the little summary tool does come in handy. Plus the setMaxInactiveInterval is great. With CF8+ you can also read information from other sessions, it'd be easy enough to pull out a username from a session to more easily identify them. That way you'd know who you were marking for expiration ;)


Feb 11, 2010 at 5:02 PM // reply »
2 Comments

Nice info

Thanks Ben.


Feb 11, 2010 at 8:25 PM // reply »
11,235 Comments

@David,

Hmm, I am not sure if I like the idea of having to use the absolute path; feels like it requires more of a directive than is necessary.

Have you ever encountered a browser that does not honor relative URL redirects? I have not (that I can think of).


Feb 11, 2010 at 9:39 PM // reply »
16 Comments

Great exploration of this topic. Thank you. The comments are well worth the reading also.


Feb 12, 2010 at 5:47 AM // reply »
12 Comments

I didn't read all the comments (I may do this later) but I would like to comment this a bit.

Where did you read that structClear() should be enought to clear a session? The interessting part is, that this looks like a function wich has to work like this. In older version of CF the structClear() function cleared the CFTOKEN and the CFID, too. http://kb2.adobe.com/cps/174/tn_17479.html
In my opinion this "new" behaviour makes absolute sense. Session and cookies are two different kind of, let us call it "user defined data", wich should not really belong together. So, we're saving some data in cookies, and some data in the session. CF also stores your CFTOKEN and your CFID in your session (okay thats something they could change.. for better understanding). But if you're clearing your session vars it doesn't mean you also wanna clear your cookies IMO. It could be very useful that a structClear() of a session doesnt also clear your cookies.


Feb 12, 2010 at 7:51 AM // reply »
11,235 Comments

@Roman,

I agree with you that this behavior does make sense for all the same reasons; as you put it, the Session scope is just another place to store data - it's not the primary connection.


Feb 12, 2010 at 10:03 AM // reply »
11,235 Comments

Here is a follow-up post on various approaches to actually killing the ColdFusion session (building on this post and the comments):

http://www.bennadel.com/blog/1847-Explicitly-Ending-A-ColdFusion-Session.htm


Nov 23, 2010 at 5:19 PM // reply »
1 Comments

Hay Ben,

I find this very interesting I am running on Coldfusion 8 server and have copied your code and tried this out.

I can clear the session varibles but when I go to clear the cookies.
It still has the same CFID.

So I though I should try again.
I cleared the browser cookies and tried again in a new browser and the CFID is still there...

What am I doing wrong? Is there a server check I need on. I am getting the same results from my other application I have created and it is driving me crazy.

Thanks in advance,
David.



Post A Comment

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.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
May 14, 2013 at 6:57 PM
The UX Of Prototyping: Low-Fidelity Is The New High-Fidelity
@Phillip, I'm not sure I follow what you mean? Are you saying that you looked at the list of widgets provided by the jQuery UI and let that be your style guide? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools