The Same CFID-CFTOKEN Values Are Used Across ColdFusion Session Timeouts
In my other post on handling ColdFusion session management with asynchronous page requests, Lee brought up the point that a user will use the same CFID and CFTOKEN values even after their session expires. Meaning, that if the user makes a page request after their session has expired, ColdFusion will open a new session for them and use the same CFID and CFTOKEN values from their previous, expired session.
I didn't doubt this, but at the same time, I had never thought about it and had never seen this fact for myself. As such, I figured I would run a quick demo to see if this actually happens. All I needed to do was set up a small, simple Application.cfc that has a short session timeout and logs the start and end of each session:
<cfcomponent
output="false"
hint="I set up the application settings and event handlers.">
<!--- Application settings. --->
<cfset THIS.Name = "TimeoutCookieTest" />
<cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 5, 0 ) />
<cfset THIS.SessionManagement = true />
<cfset THIS.SessionTimeout = CreateTimeSpan( 0, 0, 0, 10 ) />
<cffunction
name="OnSessionStart"
access="public"
returntype="void"
output="false"
hint="I fire when a session needs to be initialized.">
<!--- Log session stsart. --->
<cffile
action="append"
file="#ExpandPath( './log.txt' )#"
output="Started: #SESSION.CFID#-#SESSION.CFTOKEN#"
addnewline="true"
/>
<!--- Return out. --->
<cfreturn />
</cffunction>
<cffunction
name="OnSessionEnd"
access="public"
returntype="void"
output="false"
hint="I fire when a session needs to be ended.">
<!--- Define arguments. --->
<cfargument
name="Session"
type="struct"
required="true"
hint="I am the expired session scope."
/>
<!--- Log session stsart. --->
<cffile
action="append"
file="#ExpandPath( './log.txt' )#"
output="Ended: #ARGUMENTS.Session.CFID#-#ARGUMENTS.Session.CFTOKEN#"
addnewline="true"
/>
<!--- Return out. --->
<cfreturn />
</cffunction>
</cfcomponent>
With this Application.cfc in place, I ran the page, waited for the session to timeout, and then ran the page again. After a few bouts of this, my log file looked like this:
Started: 7501-57161699
Ended: 7501-57161699
Started: 7501-57161699
Ended: 7501-57161699
Started: 7501-57161699
Ended: 7501-57161699
As you can see, even as the one session ends and the next begins, ColdFusion uses the same CFID and CFTOKEN values stored in the user's cookies.
Seeing this, I wondered what would happen if I manually changed my CFID / CFTOKEN value and made another page request? So, using my Web Developer Toolbar, I opened my page cookies and changed my CFID value to:
1234
Then, I refreshed the same page, waited for timeout, and checked the log:
Ended: 7501-57161699
Started: 1234-57161699
Ended: 1234-57161699
As you can see, ColdFusion picked up my manually edited CFID value and started using that in the new SESSION management.
Anyway, this isn't really stuff that needs to be thought about, but it's nice to know how the cookies are being handled under the covers.
Want to use code from this post? Check out the license.
Reader Comments
It's always interesting to know what happens under the covers ;).
@Francois,
Ha ha :)
This sounds very unlikely.. but what if you edit the cookie values to match that of someone else's session, are you able to hijack a session this way?
(Again I can't imagine that anyone would be able to exploit this, but just curious.)
@Tim,
Unfortunately, I think that is exactly what happens. This is why you hear about crazy problems where a search engine accidentally spiders a site that has URL-based CFID/CFTOKEN values. Remember, sessions can still work without cookies as long as the user puts the CFID/CFTOKEN values in the URLs (hence the AddToken boolean in CFLocation).
This is also why security audits won't pass if you use the built-in CFID/CFTOKEN values as unique identifiers.
Nice investigation Ben. If you think about it this behaviour makes sense but is unintuitive; the client tokens identify the client, not the session. A session simply represents a period of time in which a particular client is active. Just don't use client tokens for information which is login/session sensitive.
I have kind of learnt this the hard way.
@Darren,
Right, exactly. It makes sense, but it's the kind of thing you never really have to think about, so you don't really know if it's intuitive or not.
@Ben
Duh.. I knew that. Well, I knew that adding it in the URL passes the session information along, I should have figured the cookies would do the same thing.
@ Tim (and @Ben), yes it is exploitable.
Note: There is a tick option in ColdFusion Administrator 'Use UUID for cftoken'.
This makes the cftoken unique and harder to guess. I haven't found a reason not to use this setting ever. Has anyone?
For reasons described here, we decided 5 or 6 years ago to abandon the CF session management. The fatal flaw in the system was clueless users copying and pasting urls from their browser address bar and emailing them to others. Result was hijacked sessions with users doing things they shouldn't have been allowed to.
@Jeff
Sounds to me a bit like throwing the baby out with the bath water.. Why not just make sure to set addToken to no in all cflocations? Wouldn't that keep it our of the URL? (And be alot easier then reproducing that functionality.)
Though I am curious, how did you brew your session management?
@Tim,
Agreed. You still need to leverage ColdFusion as the base session management as it creates your memory spaces on the server. However, I think we can take steps to make ColdFusion session management much more secure (via setting our own cookies and a host of other tweaks).
@Tim
That's where we started, but since we were government, we had very strict rules about usage of cookies. The other piece that tipped the scales was that CF was apparently reusing the token id's which was causing big problems, for obvious reasons. I talked to some Macromedia guys about it and they suggested I use the UUID's, but, alas, we were still on CF5 and it wasn't an option, so we reluctantly rolled our own.
We did it by building a few custom tags which were called in application.cfm and onRequestEnd.cfm and kept everything in the db. I'm not sure we would go that route again knowing what we know now. The good side is that we totally know what's going on under the covers.
...Ahh CF 5.
Makes me nostalgic about switching from Allaire ColdFusion Studio 4.5.2. To Macromedia HomeSite 5..
Anyway I see your point Jeff. CF's come a ways since then it seems.
You can also use cfcookie to manually set the cookies to expire immediately. That way when the user closes the browser the cookies are gone.
@Lee,
I believe for "Browser Session cookies", you need to set the cookie to have no expiration date (meaning, you exclude it from the CFCookie tag).
Couldn't you also use a database to store client data? That would be much more secure than a cookie. From what I remember CF5 does let you use the db to store client dat.. Another idea would be to use browser session-only cookies which would be deleted as soon as the browser is closed.
@Larry,
I haven't dealt with CLIENT variables in a long time, but yeah I believe you can store them in the database. But, I think the browser still needs a way to associate itself with the session on the server; I think that aspect still needs cookies (but I am not nearly sure)
@Ben,
Yes, that is correct.
I deal a lot with these kinds of issues since I sell 3rd party software and have to handle all kinds of setups that people will use it in...like supporting shared SSL (which requires passing the session ID in order to not lose the session). I use a variety of other little tricks that at least help reduce accidental sharing of links with the session IDs on them (and I hide them from search engines). I also use a setting so that for sites that use a dedicated SSL that improves the security, and also check for a CFID or CFTOKEN being passed in a URL or Form, and automatically reset them manually. Also set a secure random cookie each time the user logs in and make sure it exists before giving them access to anything. So there's a lot of things you can do if you want to beef up security even if you use the built-in session management.
I have heard of people also checking that both the session and the CGI.Remote_Addr stay consistent to improve security. Appearently there were some issues with this for AOL who passed their users around between IPs. remote_addr may be the same for people in a single location also, but it should handle a larger percentage of the malicious hijacking.
@Mhunnell,
That's interesting re: AOL. I never thought of a session actually changing IP address mid-session. But yeah, I have seen people use the IP tracking to make sure that every request comes from the same IP.
I would think that AOL is such a secondary source of connection now that it probably is not worth worrying about.
I'm at an odd crossroad here. Up until now, I've kept my application off-limits to search engines. I've used a couple your techniques for giving them short sessions and such. Been working well.
With respect to human users, I've been VERY diligent about using URLSessionFormat to keep session variables across page requests with cookies disabled. Also been working well.
So here's my quandry - I now want open up my application to allow search engines to index. However, I've got session variables embedded everywhere in my URLs! So what you've noted above is going to occur: the robots will grab all of these URLs, index them, then pass them as hijacked sessions through their results and I won't be able to track new visitors!
Bottom line: how can I make the URLs NOT pass the session management variables when it's a search engine?
By the way, I've seen mention of using two different CFAPPLICATION declarations, but haven't explored it fully. Sounds good, but what's going to short-circuit that way? Are all of my URLSessionFormat-ed link going to die if I turn off session management for robots?
@Bob - well you first might want to decide if allowing users to turn cookies off is worth lowering the overall security of the site. There's simply no way around the fact that it can be a problem once you start passing those session tokens around.
The way I've managed this particular situation in the past is simply to check the user agent string for all the common search engine terms - hard to get all of them, but you can hit at least the 20-30 most common. Rather than use URLSessionFormat, I use a variable appended on the end of all URLs for the session info, if I find a search bot, that variable is left blank. Additional code is used to check for whether cookies are on or off.
So far I've not found an easy way to do this without doing the session management more manually.
@Bob,
Hmm, I don't have a great suggestion for this. You will probably need to re-architect the way ALL the links work.
As an extreme stop-gap, what you might be able to do is capture the website output *before* it gets flushed to the browser and strip out the targeted url parameters (if the request is coming from a bot). Of course, that definitely has performance issues and feasibility issues (are you sure you can grab all bots??).
Well, what it came down to ended up being to just go back and strip out all of the URLSessionFormat() links. A bit of a pain, but...
It was definitely better going into the future to be able to allow search engine spidering. Those users without cookies can still use most of the application without them and I will just give them appropriate warnings for the functions that require them.
Of course, I had hoped for a quick fix because we're all lazy geeks, right? :)