Ask a Jedi: cflogout, session variables, and the back button

This post is more than 2 years old.

Steph has a few questions/concerns about cflogout, sessions, and the back button. His email to me was:

Hi Ray, What's the best practice for logging out users using cflogin and cflogout? I'm using CF7, and I cant figure out how to workaround the following issues listed in livedocs:

Caution: If you use web server-based authentication or any form authentication that uses a Basic HTTP Authorization header, the browser continues to send the authentication information to your application until the user closes the browser, or in some cases, all open browser windows. As a result, after the user logs out and your application uses the cflogout tag, until the browser closes, the cflogin structure in the cflogin tag will contain the logged-out user's UserID and password. If a user logs out and does not close the browser, another user might access pages with the first user's login.

Also....

In many cases, you can effectively end a session by clearing the Session scope, as shown in the following line. The following list, however, includes important limitations and alternatives:

<cfset StructClear(Session)>

Clearing the Session scope does not clear the session ID, and future requests from the browser continue to use the same session ID until the browser exits. It also does not log the user out, even if you use Session scope storage for login information. Always use the cflogout tag to log users out.

Well, I use cflogout, but if you use the back button you are still able to access the protected pages, and session variables are maintained.

What's the best way to completely log a user out (kill session variables, etc) while keeping the browser open?

There's a lot going on there, so let me try to pick it apart. First off, the issue mentioned in the live docs quote above (direct link here) relates to the fact that ColdFusion's CFLOGIN system will automatically connect to any web server level security enabled. My readers know that I've been a bit ticked at CFLOGIN for some time now. I once lost a whole day trying to debug an issue with BlogCFC that ended up being related to this. There is no way to disable CFLOGIN's ties to web server security. That means trouble if you don't want to use it. This is why I do not recommend using CFLOGIN anymore. I still use it in my OS applications but I'm slowly rolling it out. (The last update to Soundings removed it.)

So with that in mind - I'd just stick to simple session variables. That removes any problems with the web server level security. It's no longer an issue. But then that leaves your last issue:

Well, I use cflogout, but if you use the back button you are still able to access the protected pages, and session variables are maintained.

Technically, the session variables weren't maintained. What you are seeing is simply the browser's cache being used to redisplay an old page. The session variables on the server were really cleared.

So how do we stop the pages from being cached? You have to remember that the browser, not you, is the ultimate judge on what it will and will not cache. We can tell the browser to not cache, but that doesn't mean the browser won't ignore your request. The user could also just save the HTML to the desktop. It would be dumb for a user to save a sensitive page of credit card information to their desktop, but I bet we all know a few users who would do that.

With that in mind then, the code to tell the browser to not cache is rather simple, and is described at the ColdFusion Cookbook entry: How can I prevent a browser from caching my page?

<cfheader name="cache-control" value="no-cache, no-store, must-revalidate"> <cfheader name="pragma" value="no-cache"> <cfheader name="expires" value="#getHttpTimeString(now())#">

Steph, hope this helps!

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Dave Ferguson posted on 1/2/2009 at 9:43 AM

There is also another issue with caching that Ray does not mention. There are TONS of client installed 3rd party caching tools to speed up internet browsing. A vast majority of these will actually ignore the http header settings and cache anyway.

I have also personally ran into issues with network based caching systems. These do just the same as client software but on a larger scale.

I don't really have a good way around these so sorry for that. I just wanted to bring them up in case someone else might.

--Dave

Comment 2 by Phillip Senn posted on 1/2/2009 at 6:50 PM

I seem to remember you saying to not use structClear(session) as well, but to use structDelete instead.
Something about how structClear will delete the sessionid and cfid and cftoken.
But that was years ago.

Comment 3 by Raymond Camden posted on 1/2/2009 at 7:21 PM

Yes, that was true years ago, but not since CF6 I believe.

Well wait, I stand corrected. :) It removes cfid, cftoken, and sessionid. It looks like structClear is still "naughty". Everything worked fine though with this sample code:

<cfif structKeyExists(url, "clear")>
<cfset structClear(session)>
</cfif>
<cfparam name="session.hits" default="0">
<cfset session.hits++>
<cfdump var="#session#">

I could clear the session and still add values to hits. But if I had written any code to depend on sessionid, it would fail. Oddly, urltoken, one of the 'builtin' session variables, was also nuked on the FIRST hit with clear=1 in the URL, but returned after clear was removed. So CF 'fixed' up one built in var but not the others. Also, the values in URL token matched the CFID/CFTOKEN I had before.

So I'd probably not recommend doing a struct clear on session now.

Comment 4 by Steph posted on 1/2/2009 at 9:05 PM

Thanks Ray!

You mentioned that the session variables are cleared on the server but maintained in the cached page. I logged out and then used the back button until I reach the first page that I accessed after logging in. When I do a shift-refresh of this page, I am then able to proceed to other pages as if I were logged in, and the session variables are still available per the debug info.

I changed my app so that the login page points to a redirect page that points to the first page of the app. This seems to solve the shift-refresh problem.

Are there any plans to fix the cflogin issue with the next CF version?

Steph

Comment 5 by Raymond Camden posted on 1/2/2009 at 9:25 PM

Actually no, I said the session variables were NOT maintained. You have to remember that there is a difference between the actual variable, session.name, and the _display_ of that on a page. If I logon, and end up at a page that does #session.name#, and the value is Ray, I can keep that web page on my computer for years. (If I don't reload.) The word "Ray" is still there, but the session variable timed out long ago. So when you see the browser get the page from cache, it is just getting the HTML from it's cache, not the actual session variables.

You mentioned you were able to shift-refresh on a page. Was that page the result of your login? (You said you went to the first page.) If so, what you see is the browser re-POST-ing the form details from the login, so in essence, you logged in again.

Making your login page do a cflocation does help because then the user can't reload the page to relogin.

This is not broken, Steph. It's just the nature of the web, browsers, and your cache settings.

Comment 6 by Ben posted on 2/23/2009 at 3:24 AM

thanks Ray! I've always enjoyed learning from your writings and posts. I have a couple of related questions that you might know the answer to:

1. how to force CF to end a session on browser close? the session structClear is working great for me, but sometimes the user just closes it.

2. keeping a session variable consistent between https and http pages. Sometimes CF seems to come up with a new session ID when going from HTTP to HTTPS and back. Undoubtedly, its my misunderstanding of proper usage ... but I figured a jedi might know.

thanks again for all your good work and sharing!

Comment 7 by Raymond Camden posted on 2/23/2009 at 3:59 AM

1) To be clear - you can't END a session. CF ends a session when it has been inactive. You can, however, make it so when the user closes his browser and comes back, he gets a new session. Just use J2EE sessions in the cf admin. There are other ways, but that is the easiest.

2) That's because, technically, it is a different site. If you are using APplication.cfc, try using setDomainCookies=true. I believe it changes the cookie from foo.main.com to *.main.com. I always forget the exact change so forgive me if that is wrong.

Comment 8 by David posted on 4/6/2009 at 7:23 PM

If the user logs out, I don't want them hitting the back button to see the previous page. So I'm use the cfheader code as outlined in this post.

Firefox and IE seem to respect this very well, however, Safari does not. In other words, I can hit the back button in Safari (3.2.2) and still see the previous cached page instead of the app redirecting to the login page (as Firefox and IE do so well).

Has anyone else experienced Safari not obeying cfheader? If so, how did you make Safari take orders?

Comment 9 by Raymond Camden posted on 4/7/2009 at 7:11 AM

There are META tags that also relate to caching. I can't remember them offhand, but you can try using them as well.

Comment 10 by Phillip Senn posted on 4/7/2009 at 9:56 PM

<cfheader name="Expires" value="#GetHttpTimeString(Now())#">

Comment 11 by David posted on 4/7/2009 at 10:15 PM

Raymond, the meta tags don't make a difference for me.

Phillip, I'm already using that code:
<cfheader name="cache-control" value="no-cache, no-store, must-revalidate" />
<cfheader name="pragma" value="no-cache" />
<cfheader name="expires" value="#getHttpTimeString(now())#" />

Comment 12 by Raymond Camden posted on 4/7/2009 at 10:18 PM

Hmm. Well, at the end of the day, this may be a browser issue. Just because the server says, Dont Cache, and the HTML says, Dont Cache, it doesn't mean the browser has to follow the rule.

Comment 13 by Jody Fitzpatrick posted on 6/15/2009 at 7:47 AM

Hey Ray

I use a really different method when it comes to processing security, I do however use cflogin and cfloginuser but I also create a UUID and log the ip into a database and if the UUID doesn't match the ip I send them to the logout processor. Is that a smart way to process security?

Comment 14 by Raymond Camden posted on 6/16/2009 at 7:19 AM

Hmm. Seems kind of nice. I'd probably store the info in the app scope though. Don't see a need to persist it like that unless you have a cluster.

Comment 15 by Brian Lang posted on 8/4/2009 at 10:10 PM

I'm looking for a way to modify the CFID and/or CFTOKEN cookies set by CFLOGIN. These cookies expire 30 years after login. I'd like them to expire when the browser closes. What should I change?

Comment 16 by Raymond Camden posted on 8/5/2009 at 1:39 AM

cflogin can be set to tie to the session scope, which means it will end when the session ends.

Comment 17 by Brian Lang posted on 8/5/2009 at 1:52 AM

How would you do that?

Comment 18 by LearningMore posted on 8/6/2009 at 11:50 PM

I'm developing a web based app & I'm making use of sessions, no cookies, for security. It works fine & users can successfully logout. However, & this is a pretty serious issue: if you click on the back button you are still able to access the protected pages.

I've pretty much tried the CFHEADER tags on each page.

<cfheader name="cache-control" value="no-cache, no-store, must-revalidate">
<cfheader name="pragma" value="no-cache">
<cfheader name="expires" value="#getHttpTimeString(now())#">

This really helped for Firefox3.0.12 & for Netscape Navigator9.0b3 BUT NOT FOR Opera 9.64 & very importantly NOT FOR Internet Explorer 7.0.6001.18000.
A major concern is that majority of web users use IE!

Please can anyone provide a tried & tested permanent solution please? It'd be really appreciated. Thanks

Comment 19 by Raymond Camden posted on 8/6/2009 at 11:54 PM

At the end of the day, the browser, not you, decides how to cache content. Those header tags tell the browser to not cache, but the browser can ignore it at will. Not only that, I can simply save your pages to the desktop if need be. Have you tried perhaps the META tag as well? While it has the same issue, it may work in IE better. Just note you can't depend on it.

Comment 20 by Learning More posted on 8/6/2009 at 11:59 PM

Thanks ray, I tried them but will again. However I noticed yahoo had the same issues years back but not anymore. I'm cerain theres a solution out there somewhere. I'm certain the session variables aren't stored after logout though so it's a cache issue & interestingly upon page refresh after the use of the back button, the page access is denied. I guess this might provide a much better clue. Thanks

Comment 21 by Learning More posted on 8/7/2009 at 12:11 AM

I included the following but I noticed no difference

<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="Mon, 07 July 2009 00:00:00 GMT">

Comment 22 by Raymond Camden posted on 8/7/2009 at 12:13 AM

Then I think you may be out of luck. Again - it really is a client side issue that you can _never_ be 100% sure of. You could include Ajax code that pings the server, notices a session timeout, and pushes the page away, but even that can be surmounted if the user disables JS.

Comment 23 by Just Learning posted on 8/7/2009 at 12:19 AM

I thought of coding to [force] clear the browser cache afterward but the concern is other user data. I wouldn't want that to happen now. I was just thinking: what if the app runs in a browser window w window which does not show any buttons?, you know like the one for typing in posts(this particular one on this site)? I guess that will pretty much work. Does coldfusion have any specific tags or any code for that? Thanks

Comment 24 by Raymond Camden posted on 8/7/2009 at 12:24 AM

That's all client side JavaScript, opening a window with no chrome. The closest thing ColdFusion has is cfwindow.

Comment 25 by Just Learning posted on 8/7/2009 at 12:42 AM

OK, CF8 stuff.Just thinking: is there something like the cfscript variant of the javascript window.open ?Thanks

Comment 26 by Raymond Camden posted on 8/7/2009 at 12:49 AM

Nope - closest is cfwindow.

Comment 27 by learning More posted on 8/7/2009 at 1:03 AM

Alright, thanks

Comment 28 by Tim posted on 10/16/2009 at 8:01 PM

I have always used session variables as a means to determine who is logged in... now that most users are familiar with multi tab browsers, I have an issue with users logging in with different user accounts in the same browser window (but separate tabs.) As we know session vars are shared between the tabs causing grief in this situation. Making matters worse, what if the user opens a third tab and copy/pastes the url. This really limits the use of session vars.

Anyone have any thoughts on managing session vars in multi tabs in the same browser window?

Comment 29 by Raymond Camden posted on 10/17/2009 at 8:05 AM

You have to remember that a tab is nothing more than another request. The server has no way to differentiate one tab request from another. The only solution would be to not use cookies for your sessions and use URL params, but even then, if the user opens the new tag and pastes in the url with the params, you are in the same situation.

In my opinion, this is _not_ a limitation of session vars.

Comment 30 by Tim posted on 10/17/2009 at 5:35 PM

It's too bad there is no way to identify browser tabs... and I agree it's not a limitation directly on session vars as session vars have not changed. The environment of a single browser session has changed with the introduction of multi tabs.

Without a way to identify tabs, IMO the practical use of session vars is greatly compromised.

Any thoughts are appreciated. - Tim

Comment 31 by Raymond Camden posted on 10/17/2009 at 5:38 PM

But I think here you have a unique situation - users going out of their way to use the application incorrectly. Users will always be...well, users, but personally, I've never heard of folks doing that.

There is one simple solution - if a user is logged in, don't let them log in again. Tell them as much and then provide a link to the logout page.

Comment 32 by Tim posted on 10/17/2009 at 6:10 PM

I think users may be attempting efficiency with multi tabs not realizing the application was not designed for multi tabs.

Yes - only allowing one log in per window session will provide as a solution for the multi tab multi user issue. However there are many more potential situations other than just the log in.

Guess my point is CF developers should consider the potential issues with browser tabs when using session vars... (still wishing tabs could be identified.)

Thanks Ray

Comment 33 by Madhu posted on 6/4/2011 at 6:09 AM

Hi Ray,
I have a form page, an action page and a confirmation page.

I submit the form page...takes me to the action page where I validate the fields...the fields are NOT valid..I place the data & the error message in session and redirect(cflocation) to the form page. In the form page I store the session variables(err message and bad data) in local variables, destroy the session variables and use the local variables thereafter.

Now I correct the values and submit the form again...goes to action page...validates and then redirect to the confirmation page.

Now if I use my browser back from the confirmation page, it takes me to the form page with the validation error message and bad data...I understand that the IE 7 browser is using the cache to get the data..but I tried using cfheader and META tags to avoid pulling from cache...still the browser pulls from cache...this doesnt happen in Firefox

Any thoughts on how this can be resolved?

Comment 34 by Raymond Camden posted on 6/4/2011 at 11:21 PM

As just an FYI, I normally recommend against multiple pages like that. I'd probably use one page that just renders based on what the current state is. But that's a different story. :)

So normally the cfheader thing should work for IE. Can you share the code you used?

Comment 35 by Madhu posted on 6/7/2011 at 12:51 AM

Ray,
I am using the following code:

<cfheader name="cache-control" value="no-cache, no-store, must-revalidate">
<cfheader name="pragma" value="no-cache">
<cfheader name="expires" value="#getHttpTimeString(now())#">

<META HTTP-EQUIV="expires" CONTENT="-1">
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="cache-control" CONTENT="no-cache, no-store, must-revalidate">

Also, I am interested to know if there is a specific reason (like its a bad practice..) on why you are against using multiple pages. It will be a learning for me.

Thank you for your time!

Comment 36 by Raymond Camden posted on 6/8/2011 at 6:06 AM

That code looks fine to me. At this point I'd just blame IE. That doesn't help you though.

In general, I don't use an 'action page' for a form. I use a self posting form. Here is an example I just wrote. It may not work - I didn't actually run it I just did it from scratch:

http://pastebin.com/iC0JUAvm

You can see how the form posts to itself. Code on top does quick verification (the form has one field so its simple). If the form is ok (the one field is ok), then we set a flag to not show the form.

This is just an example though.

Comment 37 by Madhu posted on 6/13/2011 at 9:32 PM

Thank you so much and Sorry!

I thought I replied already but when I checked the site for a response from you....thats when I realized I didnt even reply to your message...very sorry abt that.

Thank you for your example, I am aware of "posting to the same page" method...

Sorry if I was not clear with my question, I wanted to know why you thought the "posting to same page" method was better than the multiple pages method?...these are facts only experienced people can explain....not the books :)

Comment 38 by Aaron Starner posted on 10/25/2011 at 7:19 PM

How about killing all sessions for a single CF application, just once? I need to "refresh" the session scope, but not for the entire server, just the one application. (Using application.cfc, BTW.)

Comment 39 by Raymond Camden posted on 10/25/2011 at 9:33 PM

I don't believe it is possible. CF9 added applicationStop(), which 'resets' an application. When run, the next hit will execute onApplicationStart. But it doesn't reset sessions.