Session token rotation - Security Series #12.3.2 and #6.4.2

This is a continuation of a topic that I have been blogging about and thinking about for a long time. Session management and cookie security are really interesting topics that I could yammer on about for hours. Go ahead, ask my wife.

About 3 months ago, Ben Nadel blogged about a behavior of ColdFusion that I found troubling.

The behavior is that if the ColdFusion server is presented with a cookie that contains a session token that was ever valid (since last server restart), ColdFusion will reuse that token to create a new session.

ColdFusion is not going to reinflate the session with any old data, so we do not need to worry about any information leakage there. It actually seems to create a new empty session, and so at first I wasn't sure why it bothered me. If a token became compromised, a hacker could populate their browser with it, then go to the site and get a new session. So what?

Then it occurred to me what the vulnerability was. And I think my instinctive reaction was correct.

The Vulnerability

If you keep using the same session token with your application, and that token becomes compromised, then your session can get hijacked anytime between when you log into the application and when your session times out. Any of your sessions. Ever. As long as you're using that same token.

If I am a hacker, and I have compromised your session token (by whatever means: XSS, social engineering, packet sniffing), then I can create a script that will hit the site that is being hacked ever N minutes with that token, then just leave that for a few days.

Every time I hit the site with the compromised token, it will either create a new session or persist an existing session. Eventually, you will log onto the site again. After that, every time my script hits the site it will persist your session, then I can, at my leisure, go back to the site with your valid, and active, session and impersonate you.

It's kind of confusing to think about. I hope my explanation is clear enough (but I doubt it).

So what can we do about this vulnerability?

The way to mitigate the risk of this vulnerability is to rotate session tokens. Every time you start a new session, it should be started with a new session token. This is easier said than done. You cannot simply ask CF for a new token. Cookies need to be replaced, which requires multiple HTTP messages.

The way I am proposing to solve this problem is experimental. I've thought long and hard about this one and this is what I have come up with. I have not implemented this anywhere, and I have not done a ton of testing with it. I would love to get opinions on this, I am not sure it is a great solution, or even a good one.

Application.cfc


<cfcomponent output="false">
<cfscript>
this.name="SIDRotate22";
this.sessionManagement = true;
this.sessionTImeout = createTimespan(0,0,0,30);
    this.setClientCookies = false;
</cfscript>


<cffunction name="onSessionStart" access="public" output="false">
<cfif NOT StructKeyExists(URL, "sessionInitialized")>
<cfcookie name="CFID" value="" expires="now" />
<cfcookie name="CFTOKEN" value="" expires="now" />
            
<cflocation url="#CGI.script_name#?sessionInitialized" addtoken="false" />
        <cfelse>
            <cfcookie name="CFID" value="#session.CFID#" />
<cfcookie name="CFTOKEN" value="#session.CFTOKEN#" />
</cfif>
        
</cffunction>

</cfcomponent>

In this Application.cfc I have first told ColdFusion not to set the token cookies (this.setClientCookie=false). I will take care of that. Then, in my onSessionStart() method I check to see if a sessionInitialized URL parameter was sent. If the sessionInitialized parameter does not exist, then I know that I have not explicitly reset the cookies yet, so I use the <cfcookie> tag to expire the CFID and CFTOKEN cookies that might be passed. Then the method redirects the user back to the same page they just requested, this time with no session cookies but with the sessionInitialized parameter.

When the redirected request hits the application, onSessionStart() will be fired again, because no valid session cookies will be presented to it. This time, since the sessionInitialized parameter will be present in the URL string, I will not expire the cookies, instead I create new session token cookies with the new CFID and CFToken that CF has created. Now that a valid session with valid session token cookies have been created, onSessionStart() should not run again, so we should not need to worry about adverse effects from that happening.

Life I said, I am not sure if this it the best solution, but I cannot think of another way to rotate the session tokens effectively.

The one downside to this method, and maybe someone can help me figure out how to address this, is that it is very difficult to detect if the user has cookies disabled so that I can give them a proper warning that they must have cookies enabled for the application to work.

As with all session management issues in ColdFusion, we also need to worry about how this will work with JEE sessions. We do not have as much control over the JSESSIONID cookie as we do with the CFID and CFToken cookies. I've tried it out with JSESSION tokens and it seems to work OK, but I have not done much testing with it.

Comments
Pete Freitag's Gravatar Thanks for posting this Jason.

I suppose we can thank client variables for this behavior :-|

If you use cookies that expire when the browser closes, then you would only have this risk as long as the same browser window was open, after that they would use new tokens.

This is certainly a risk when you have cookies have a long life time but if you shorten their life I don't think there is much risk, do you agree?
# Posted By Pete Freitag | 6/26/09 2:39 PM
Jason Dean's Gravatar @Peter, thanks for bring this up. It's something I have been meaning to blog about. You're right in that if you use non-persistent cookies that it should mitigate the risk effectively, except that it seems that when you use the "Save and Close" feature in browsers that support it, that they persist the cookies anyway, and I suspect that those features are not going to go away.

I have not tested the "Save and Close" thing recently, but I recall trying it out when it was first introduced in Firefox and it seemed to persist the cookies. Perhaps that is too narrow a concern, but I use that feature a lot, and it would not surprise me if I have been using the same session token on my blog for several weeks because of it.

I guess this is also one of those situations where we need to ask if we can trust the browser to always behave the way we think it should.
# Posted By Jason Dean | 6/26/09 2:47 PM
Pete Freitag's Gravatar @Jason Yeah I think explicitly setting the expiration of the cookie (to match the session timeout) is actually better...

I never thought about it until I wrote that comment, but I leave my browser open all day, sometimes for days at a time.
# Posted By Pete Freitag | 6/26/09 2:52 PM
Brian's Gravatar I suppose the only thing that bother me about this is that if someone has (legitimately) deep-linked into your site, you've now broken the link with the cflocation. Surely there's a better way... (I personally despise cflocation, but that's just me -- it seems to be such a cop-out)

<shrug>
# Posted By Brian | 6/26/09 6:42 PM
John Whish's Gravatar Really interesting post. If you do set the expiry of the cookie to be the same as the sessiontimeout then you're going to have the keep updating the cookie on each request aren't you? The session times-out after a period of inactivity, whereas the cookie will time-out from the time it was created.
# Posted By John Whish | 6/29/09 3:35 AM
Jason Dean's Gravatar Thanks everyone for your comments.

@Brian, I've address your comment in a second post. http://tinyurl.com/r7nc98 I think you're right on cflocation. This time I am using cfhttp, which feels a little better, but it only works with CFID and CFToken. For JSESSIONID tokens, I still need to do some more research, the redirect may still be necessary.
# Posted By Jason Dean | 6/29/09 9:59 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner