Friday, May 8, 2009

ColdFusion Start New Session - Not so hard?

So there have been issues with sessions on logout or timeout. Dealing with the issues for a long time. I can't believe it's this simple. I've never been a big fan of dealing with cookies, but that, my friend, is the answer. Take a look:

This is partly why I love ColdFusion, it takes care of all session management for you. Settings are in the ColdFusion Administrator, piece of cake. We don't even think about cookies. but depending on your CFAdmin settings it stores more or less information in those cookies that we don't have to worry about. But cookies are required for sessions, we can't get around that unless you want to take the painful route of adding your tokens in the URL of every internal link in your site. I hate it, but whatever. Back to cookies.

On logout, I and many others have resorted to this (or similar) code to end a session

<cfset StructClear(session)>

Which other have argues is not good practice. But googling the answer doesn't turn up good results.

Here is the answer:

<cfcookie name="JSESSIONID" value="deleted" expires="NOW">
<cfcookie name="CFID" value="deleted" expires="NOW">
<cfcookie name="CFTOKEN" value="deleted" expires="NOW">
And I'll explain why.

Digging into the guts of Coldfusion, the Java guts, and discovering what is actually going on in the session management is interesting and frustrating. But lets take a look:

<cfset variables.tracker = createObject("java","railo.runtime.SessionTracker")>
<cfset variables.sessions = tracker.getSessionCollection("my_application_name")>
<cfdump var="#variables.sessions#">

This will show is us a lot of this:

After doing the <cfset StructClear(session)> it looks like this:

As you can see, we have cleared out all of the actual data members but we have not deleted the actual session allocation in memory. You could say we accomplished 90% of what we intended by using <cfset StructClear(session)>. But the, if we were doing session monitoring using the session tracker we lose that data and may still have problems with the users login because their browser still thinks they have those CFID and CFTOKEN values. <-- that is the underlying issue with using . Crazy things happen. Usually the session tries to be used again and the URLTOKEN get set their cookie, but the CFID and CFTOKEN are missing. Unless you want to to other additional code checks you are going to have issues.

That's why the solution is to clear the JSESSION, CFID, and CFTOKEN cookies. The server generates a new session, the old session data is available but orphaned.

If you wanted to be reeeeeeealy safe, just in case there are cookie hijackers out there and they stole the user's cookie info and were able to duplicate all header info, cookie and IP stuff to spoof the server, and the server didn't catch it, then you would do both <cfset StructClear(session)> and clear the cookies.

Am I the first to really document this simple and straight forward? I can't be,,,

Tuesday, May 5, 2009

Coldfusion List Validator

When working with coldfusion lists from user-entered-data, or from a database of user-entered-data, it's very important to make sure it's a valid data and validly formatted - one comma between each value, only numeric (if desired - as in this case), and no leading or trailing commas.

Coldfusion is abolutely amazing with lists. So much easier than arrays and structures, and with instant conversion to and from an array. Beautiful! But lists can become invalid very easily, especially when working with numeric lists and using those lists in queries.

This is an easy, solid solution to validating numeric lists:

<cfset test = ",22,,345,456,7 8,4x,1,,,">
<cfoutput>#test#</cfoutput><br /><br />
<cfset test = REPLACE(REREPLACE(test,"^,*|,*$|[^\d,]","","ALL"),",,",",","ALL")>


In the case of '..,7 8,..' one might want to replace spaces with commas so that the resulting number in the list '78' is not created.

As a function:

function listval(listin) {
listin = REPLACE(REREPLACE(listin,"^,*|,*$|[^\d,]","","ALL"),",,",",","ALL");
if(ListLen(TRIM(listin)) IS 0  OR  TRIM(listin) IS "") { listin = "0"; }
return listin;


And that's a wrap.