I manage a ColdFusion-based website that recently migrated from CF 8 to CF 10. The site requires users to log in and keeps certain values in session variables, which are used throughout the site for verification, etc.
Since the migration to CF 10, I have been having a lot of trouble with sessions not "sticking" from page to page, particularly after the login process. I had not been using cookies to keep track of values on the client side prior to the migration, nor do I use addtoken="yes"
for my cflocation
tags (I'd prefer to keep the CFID
and CFTOKEN
values out of the URL).
I've been doing a lot of research on this, but am struggling with a solution.
- I've tried setting "dummy" cookies that expire immediately (see Losing session in Coldfusion), but this does not offer a consistent solution.
- I've also read that
the way
cflocation
handles requests changed in CF 9 (specifically, 9.0.1; see http://www.horisk.com/blog/index.cfm/2011/5/19/Session-issues-after-installing-Coldfusion-901-update--OnRequestEnd-behaviour-change), but even after changing mycflocation
to add tokens as part of testing, the sessions keep getting reset. - If I include the page that
appears after the user logs in as a
cfinclude
, my sessions do stick, but as soon as I click away to another page on the site, the sessions reset again (my guess is it's due to the server checkingApplication.cfm
again).
What I am doing wrong or missing? Should I not have Application.cfm
set default session values? Should I do this on the login page?
For reference, current Application.cfm
reads as follows (certain values changed for security purposes):
<cfapplication name="APPLICATIONNAME"
applicationtimeout="#createtimespan(0,6,0,0)#"
clientmanagement="yes"
datasource="DATASOURCENAME"
loginstorage="session"
scriptprotect="all"
sessionmanagement="yes"
sessiontimeout="#createtimespan(0,1,0,0)#"
setclientcookies="yes">
<cfif not structKeyExists(cookie,"cfid")>
<cfcookie name="cfid" value="#session.cfid#" expires="never" domain="#cgi.SERVER_NAME#" path="/PATH">
<cfcookie name="cftoken" value="#session.cftoken#" expires="never" domain="#cgi.SERVER_NAME#" path="/PATH">
</cfif>
<cflock timeout="5" throwontimeout="no" type="exclusive" scope="session">
<cfif structKeyExists(session,"SESSIONVARA")>
<cfscript>StructUpdate(session,"SESSIONVARA","DEFAULTVALUE");</cfscript>
<cfelse>
<cfscript>StructInsert(session,"SESSIONVARA","DEFAULTVALUE");</cfscript>
</cfif>
<cfif structKeyExists(session,"SESSIONVARB")>
<cfscript>StructUpdate(session,"SESSIONVARB","DEFAULTVALUE");</cfscript>
<cfelse>
<cfscript>StructInsert(session,"SESSIONVARB","DEFAULTVALUE");</cfscript>
</cfif>
. . .
</cflock>
My current login.cfm
page reads as follows (again, certain values changed):
<!--- check whether this variable was passed to this page --->
<cfif isdefined("form.username") and form.username is not "">
<!--- generate a hashed password from the user's entry --->
<cfset HashedPassword = hash(form.Password,"SHA-1")>
[ SQL QUERY TO CHECK USER'S CREDENTIALS ]
<cfif SQLQUERY.RecordCount is not 0>
<cfset sessionRotate()>
<cflock scope="session" type="exclusive" timeout="2">
<cfset session.SESSIONVARA = QUERYVALUEA>
<cfset session.SESSIONVARB = QUERYVALUEB>
. . .
</cflock>
<!--- put after sessionRotate() to keep IDs consistent --->
<cfif structKeyExists(cookie,"cfid")>
<cfset cookie.cfid = session.cfid>
<cfset cookie.cftoken = session.cftoken>
<cfelse>
<cfcookie name="cfid" value="#session.cfid#" expires="never" domain="#cgi.SERVER_NAME#" path="/PATH">
<cfcookie name="cftoken" value="#session.cftoken#" expires="never" domain="#cgi.SERVER_NAME#" path="/PATH">
</cfif>
<cflocation url="main.cfm" addtoken="no">
<cfelse>
<!--- no matching credentials --->
<cflocation url="login-failed.cfm" addtoken="no">
</cfif>
<!--- if there is no user name defined in the set of form variables, this is probably a spider or bot; reject it --->
<cfelse>
<cflocation url="index.cfm" addtoken="no">
</cfif>
Edit: And, lastly, here is the include that checks each user and kicks them to a timeout page if they are not authorized:
<cflock scope="session" type="readonly" timeout="2">
<cfif not structKeyExists(session,"SESSIONVARA")>
<cflocation url="TIMEOUTPAGE" addtoken="no">
</cfif>
</cflock>