New Session on Login - Security Series #12.3.1 and #6.4.1

So I was not sure whether to put this post into my Session Management discussion (6.x series) or into my Cookie discussion (12.x series), so I am doing both. This week I want to talk about getting a new session after logging into to a site/application.

So why would we want to do this?

I want to start this discussion off with a story. I like stories. many times they illustrate the points I am trying to make quite clearly.

Our story begins with the hottest new social networking web site on the planet. It is called ColdFusionCrushes.com. It is a place where high school and college-aged kids can get together and discuss the hottest ColdFusion developers in the business.

Right now, on ColdFusionCrushes.com there is a BIG debate going on between the presidents of the Ben Nadel fan club and the Todd Sharp fan club about which developer is the hottest. Needless to say, it is getting ugly.

Now clearly, both of these fan club presidents are clever girls. And one of them figures out that sessions are not being reset after login. She knows that everyday at 3:00PM (after her .NET class, since her school has not heard the news) that the other club president goes to the commons computer lab to write a blog entry and comment on the forums. She always sits at the same computer, logs in, and spends 2 hours gushing about hot developers.

So one day, at 2:50PM our clever girl who figured out the session thing, goes to the computer lab, sits at the victim's machine, and browses to ColdFusionCrushes.com. She does not log in, she just browses to the main page (so that onSessionStart() fires), then she looks at the cookies for ColdFusionCrushes.com on that machine. She sees that the site is using ColdFusion session management, so there is a CFID and a CFToken. She writes down the value of each and then closes the cookie window, yet leaves the browser itself open.

At 3:00PM our victim president sits down at that machine, browses to ColdFusionCrushes.com and logs in. She then begins her daily adoration rituals completely oblivious to what is going on in the forums in her name. Soon she receives an e-mail from the fan club vice-president accusing her of being a "hateful traitor". Later, when she figures out why, she sees that someone had hijacked her account and was using it to post nasty comments about her CF crush in the forums.

How did that happen?

Well, if you have not figured it out already. Our little hacker princess, after writing down the CFID and CFToken out of the victims browser, went over to another computer lab machine and browsed to the ColdFusionCrushes.com website. After our victim sat down and logged into her machine, our hacker opened up her cookies for ColdFusionCrushes.com and typed in the values she had written down from the victim machine. She saved and refreshed. Poof! She's logged in as the victim. She was then able to go and unleash hell on the forums.

Since the session tokens did not change, our hacker was able to easily hijack the victim's session simply by knowing the proper session token values.

The countermeasure for this is to:

  • Start a new session with a new session token
  • Transfer any important information from the old session to the new one
  • Where possible, expire the old session
  • Set new cookies into the browser with the new session token values

Now this is not the easiest thing in the world to do. It is easier than rebuilding a carburetor, but harder than learning to milk a goat. So I am going to have to do some more research before I discuss how I am going to handle this. There are a few things I need to iron out.

  • How to handle this with ColdFusion session management vs. J2EE session management
  • Determine if there are different ways to handle J2EE session management if deployed on different application servers (Jrun, JBoss, etc)
  • Figure out how to move the session info from one session to the next
  • Figure out the best way to invalidate the old session(if possible) and to write the new cookies.
  • Figure out if things are any different with Railo or OpenBD

If anyone has done any of this before, I would love to hear how you handled it. Especially with J2EE Session Management. Doing some of this on the default Jrun that comes with CF developer edition has been a pain. Jrun seems to take great issue with me trying to rewrite session token cookies. I am creating a new Virtual Machine right now to try deploying to JBoss to see if it work any differently.

Comments
Rick O's Gravatar Interesting concept ... but that's a pretty narrow attack surface. Razor thin, really. But still valid.

I wonder if having a login service would help? That is to say, having something like OAuth or OpenID where the login architecture is a completely distinct application that shares no code whatsoever with the rest of your app? In such a scenario, it would be significantly easier to start a new session when you get your new authorization token. This attack works because of the lack of line between application data and authorization data, so while a login service wouldn't inherently prevent it, it would make it easier to avoid. "Here's your authorization token and your new session token."

Of course, if you're trying to be all Web2.0-friendly and migrate session state so that your user can accomplish things before logging in ... that doesn't help all that much.

This is one of the things that has always bugged me about web app frameworks -- not just CF, but pretty much all of them. None, that I know of, support the ability to easily say "don't maintain any sort of session state until the user logs in". It causes problems with spiders and indexing, problems with shared-computer environments, problems with proxy servers, etc, etc, etc.

Interesting thought experiment.
# Posted By Rick O | 1/27/09 10:26 AM
Jason Dean's Gravatar Rick, this is not a "razor-thin" as you think. Perhaps my described scenario might seem so, but this type of session fixation attack could be more prevalent that you think. Additionally, changing the session token can protect against other types of session exploits as well.

Best practices dictate that once a user logs in, they should be switched to an SSL connection. Well, if they are in a situation where they unencrypted traffic has been sniffed and their session token has been compromised, then NOT changing the token when they switch to the SSL connection would giving the hacker doing the sniffing a key into that session.

As for the login service, I do not see how something like that would be needed. Getting a new session for the application should work just fine. Even if the old session was compromised, it would not do the hacker any good because it would be an unauthenticated session, and where possible an invalidated one.

Perhaps I am misunderstanding what you are suggesting. But regardless, this is not a thought experiment, it is a real threat. What I am suggesting is the same thing suggested by the OWASP "Leading Practice Patterns for Session Management/Integrity" http://tinyurl.com/d9cdav
# Posted By Jason Dean | 1/27/09 11:06 AM
Rick O's Gravatar My point about the thinness of the attack was just that it is, for all intents and purposes, a physical-access attack. As such, if the attacker already has that much access to the victim's machine, there are far sneakier things they can do and session hijacking should be the least of your worries. Case in point, in a shared-workstation scenario as you describe, it would be just as easy to run a keylogger. (If not easier and less prone to the vagaries of worrying about web app security.)

It wasn't my intent to imply that the attack wasn't something to worry about or didn't exist, just that cleaning up your web app is only going to go so far towards solving the overall security problem in that scenario. But, to counter my own argument, we're the guys writing the web apps so I guess we should worry about the things we can control.

Your post inspired me to look into the trickiness of getting a completely fresh set of session cookies. It turns out, there's quite a bit of blunt hackery involved. For example, you'd point your login form to this page:

<!--- Depending on your setup, maybe HTTP_HOST works better here, but I don't trust it. --->
<cfset ServerName=LCase(CGI.SERVER_NAME)>
<cfloop condition="listLen(ServerName,'.') gte 2">
   <cfloop list="CFID,CFTOKEN,CFMAGIC,SESSIONID,JSESSIONID" index="n">
      <cfcookie expires="NOW" name="#n#" domain=".#ServerName#">
      <cfcookie expires="NOW" name="#n#" domain="#ServerName#">
      <cfcookie expires="NOW" name="#n#">
   </cfloop>
   <cfset ServerName=listRest(ServerName,".")>
</cfloop>

... and then redirect from there to your actual login-logic page. The interstitial page would start you in a completely fresh session, while your form variables for your login would be preserved by the redirect. I seem to remember some issues with cflocation and cfcookie, but I think those are ancient.

Hopefully, someone can (or already has) come up with a more elegant solution than that.

(Also, sorry about the login service confusion. I was really just thinking out loud. In moving to a model where your login code was completely distinct from your application code, it would be easy to prevent such an attack in one place (the login service) and thus cover any application that depends on the credentials provided by that service. But, most CF apps aren't built that way, instead having the login code in the same app space as the rest of the code, making it harder to cut the tie and start fresh. My thoughts there weren't about a solution to the problem, more about coding practices and reducing the exposure of it.)
# Posted By Rick O | 1/27/09 11:36 AM
Mary Jo's Gravatar Is resetting the session really necessary to prevent this type of session hijacking? Why not just create a separate session var of your own upon logging in and then saving a hash of that to a cookie and making sure they match on each request? That should at least ensure that the user is the same one that logged in. Seems like some sort of check like that would be less problematic than messing about with sessions.
# Posted By Mary Jo | 1/27/09 1:05 PM
Jason Dean's Gravatar Mary Jo, what you are describing sounds like more trouble to me than what I am thinking of doing. Though I think your idea would probably work, I worry about implementing things that involve the phrase "on each request" when it comes to security. First, you need to remember to do it on each request, which may be all fine and good if you put it into onRequestStart(), but then what do you do before you log in? You either have to put in a flag to tell it not to perform that check, or you have to create a dummy cookie so that the check does not fail. That can be a lot of processing on a large site, especially on every request.

And what about requests that do not fire onRequestStart()? You need to remember to handle those properly. Custom Tags and remote methods do not fire any of the methods from Application.cfc.

By not changing the session variable, the compromised session token would still give a hacker the ability to hijack that session. What your describing might programatically prevent him from doing anything in that session, but he would still be using it. And any flaws in you logic could give them a way around your defenses.
# Posted By Jason Dean | 1/27/09 1:24 PM
Mary Jo's Gravatar I'd be interested to see your thoughts on the presentation given a couple weeks ago on the CFMeetup, that presenter was doing even more on every request, as he stored a bunch of data on each user in the database and compared it on each request...anything didn't match, and the user was logged out. Simply comparing a cookie value shouldn't be that big a performance hit...and it's easy enough to create it onSessionStart() and then just reset it on any login. As for being sure to handle the check, that would depend on your application architecture, the best way to do it may be in something other than onRequestStart.

It's just an idea to throw out...I've had my dealings in the past with messing around with resetting sessions and I'm just a bit leery of it. All this of course is also assuming that the entire site is running on SSL and a hacker can't use a sniffer to get at the cookies either. Since I'm often working with applications where only a portion of the site is protected by SSL it is more useful to me to set a unique cookie over SSL that can then be used to protect the secure areas.
# Posted By Mary Jo | 1/27/09 2:22 PM
Jason Dean's Gravatar @Mary Jo, you may have missed my point about not changing the session variable. Especially in your case, where you are switching from Non-SSL to SSL, if you do not change the sessionid, but only add the SSL ccokie that you mentioned, you are only programatically stopping someone after they have entered the session. To me this is like saying, "So what if someone has a key to my house? I will just have a robot stand in front of the door and not let them in". Hopefully your robot will never fail. Hopefully you've locked all your windows. But when looking at the big picture, that session has still been compromised.

Also, I think a security auditor would side with me on this one. Perhaps for whatever app you are working on, this solution is fine. But for an application that requires a truly secure environment, I feel that resetting the sessionid is mandatory. There is no reason you could not also do what you are suggesting as an extra measure of security, just not the only measure.

As for Jim's presentation from a few weeks ago. I did watch it after the fact. I got the impression that his applications did not have a lot of concurrent users. When I discuss solutions, I like to try to consider all application types, whether they have 5, 50, 5000 or even 15,000 concurrent users. You are right, looking for a cookie and doing a compare is not that intensive. But I am not saying that that is the only thing that would happen in onRequest or onRequestStart(). Many other things need to happen there. And, the more you add, the longer the request will take.

Session management is already doing the job we need it to, we just need to tell it when to reset the session. It can then continue to do its job without us adding anything to that.
# Posted By Jason Dean | 1/27/09 3:02 PM
Mary Jo's Gravatar I think the basic question always comes down to the level of security needed and what other security elements you have in place. You are correct, I am not typically working with highly secure applications, but then I would put your example of an online forum in the same category. ;-) Unless the entire application is behind SSL, the CFID/CFTOKEN cookies are not 100% secure and resetting them on login simply is not sufficient to prevent the application from session spoofing. But I'm curious to see if you can come up with a reliable way to do it, as it certainly would be a good additional level of security.
# Posted By Mary Jo | 1/27/09 4:05 PM
Jason Dean's Gravatar @Mary Jo, The whole point of resetting the sessionid after login is because it does secure the CFID and CFToken from being sniffed. It does not require that the entire application be on a SSL connection, only the parts after login. Once the session token has been reset and is being transmitted over SSL it can no longer be sniffed. The old token is not authenticated or has been invalidated, so it is worthless.

If you used the same session token before AND after login is when you have the problem. But if you change the session token at login and after you switch to SSL, then it is safe. I will never say that anything is 100%, but what I am describing is sufficiently safe for most applications and easy enough to implement for even simple applications.
# Posted By Jason Dean | 1/27/09 5:04 PM
Mary Jo's Gravatar @Jason - Ah well again, that would depend on the type of application you are doing, and whether the user always stays on the SSL for any future actions after they've logged in. For many applications the user login may be done over SSL (let's assume it at least is using *that* much protection), but then they are returned to the non-SSL side of the site, and may only be returned to SSL for certain actions (say, to reset a password, or for specific admin functions). This was the kind of situation I was thinking about, which is what you will see with many typical web applications that may not have highly sensitive information (but still need some protection against session spoofing).
# Posted By Mary Jo | 1/27/09 7:35 PM
Jason Dean's Gravatar Ah, I see what you are saying now. And that makes good sense. I am liking what you are saying and I am coming to like your SSL cookie. Thinking more about it and looking at some of the ways that other sites are handling it, I see where you are coming from. I am going to look into this some more.

Thank you for persisting, it definitely gives me something to think about. One thing I will point out is that if you are going to use the SSL token and NOT change the CFID and CFToken, then you must not store any sensitive data in the session scope.

All ColdFusion needs to link the client with the session data on the server is the CFID and the CFToken. It does not care about the SSL token you are setting. So the session data can still be compromised via session hijacking/session fixation attacks.

Huh. You've really got me thinking. Thanks :)
# Posted By Jason Dean | 1/27/09 8:27 PM
Mary Jo's Gravatar Well, I generally don't ever store anything sensitive in the session, but you're right this certainly not going to prevent session hijacking from happening, the idea is to prevent a hijack from being able to actually get access to the sensitive areas. And yes, it does require a little diligence in coding to do properly...but that certainly goes for a lot of these security aspects. But a combination of resetting the session on login as well as my extra SSL token might indeed be the best solution to handle sites that aren't fully behind SSL.
# Posted By Mary Jo | 1/27/09 9:19 PM
Dutch Rapley's Gravatar Jason, I'm kind of lost as to why you'd want to persist data from one session to the next. Can you please explain why? I typically set my applications to use session cookies. In my Application.cfc, this.setClientCookies = false. I then set my cookies in onSessionStart(). I set three cookies: CFID, CFTOKEN, and JSESSIONID. I then populate them with CLIENT.CFID, CLIENT.CFTOKEN, and SESSION.SESSIONID, respectively. When the browser closes, the cookies are erased from memory, as opposed to being written to the hard drive. When a user logs out, I StructClear() my session.
# Posted By Dutch Rapley | 1/28/09 4:11 PM
Jason Dean's Gravatar @Dutch,

What I am talking about is when the user logs in that I would reset the session, hence starting a whole new one. If the user, prior to logging in, has data in the old session (like a shopping cart or something like that) that I want to persist into the new session that I would need a way to do that.

Does that make sense?
# Posted By Jason Dean | 1/28/09 6:30 PM
Roland Collins's Gravatar So did you ever make any progress on this, Jason? I'd be interested in talking to you about it. We're just run into this requirement and are having an extremely difficult time getting it to work well.
# Posted By Roland Collins | 5/11/09 5:35 PM
Dharm Kumar's Gravatar You could handle this as follows:
1. on the page receiving the request, expire the existing jsessionid cookie. The do a cflocation to a new page and pass any
parameters along to the new page. This would have to be an SSL page so that parameter values are encyypted.

<cfcookie name="jsessionid" Expires="#NOW()#">
<cflocation url="https://www.yourserver.net/newPage.cfm?login=#form...; addtoken="No">


2. Do the authentication on this new page and set other session variables as required.

This seems to be working with CF8 on Firefox, Chrome.
# Posted By Dharm Kumar | 1/18/10 3:49 PM
Jason Dean's Gravatar @Dharm,

Unfortunately, this would not be an acceptable solution. In your example you use <cflocation> to redirect the user, and you place the username and password in the URL string. This is bad.

Even if you are going over SSL, this is still not secure. It is true that the query parameters will be encrypted in transit due to the use of SSL, but the query params will not be encrypted when they are stored in the browser's URL history, or when they are stored in the web servers activity logs. If either of those are compromised by a malicious user or corrupt administrator, then they will find those passed passwords in plaintext.
# Posted By Jason Dean | 1/18/10 5:20 PM
Dharm Kumar's Gravatar @Jason,

You are right about this. I overlooked these aspects in my quest to overcome the session fixation issue.

Have you had any other insights into how to tackle this in CF?
# Posted By Dharm Kumar | 1/18/10 5:56 PM
Jason Dean's Gravatar @Dharm

Not yet anything I am happy with. But I have two possible ideas.

1. Instead dof expiring the cookie and redirecting after login, do it before. Once they are directed to the login.cfm page, redirect them to a page that expires their cookies and sends them back to the login with a param that tells login.cfm not to redirect them again.

2. The other option I thought of uses cfhttp to make the second call, then using the response from that (which would receive a new cookie) to overwrite the old cookie. I need to play with this option some more.
# Posted By Jason Dean | 1/18/10 6:29 PM
Dharm Kumar's Gravatar The 1st option is what I have seen at some sites (specially banks). They ask you to enter your login on the first page and the password only on the second page. I guess they reset the sessionid on the 2nd page.

This is not an option for me. So I guess I will try the 2nd option. Will let you know what I find.
# Posted By Dharm Kumar | 1/18/10 6:38 PM
Dharm Kumar's Gravatar OK, I tried the second approach and it works. I had to import the SSL certificate into the JVM keystore. The article at http://www.talkingtree.com/blog/index.cfm/2004/7/1... helped me to get this done.

Once I had the SSL certificate, it all worked like charm. I was able to verify this on FF, Chrome and IE.
# Posted By Dharm Kumar | 1/19/10 2:55 PM
Jason Dean's Gravatar @dharm

Cool! And are you doing it all from within onSessionStart()?
# Posted By Jason Dean | 1/19/10 3:03 PM
Dharm Kumar's Gravatar actually no. index.cfm posts login credentials to checkLogin.cfm . I expire the exisitng jsessionid cookie on this page. And then make a cfhttp call to verify.cfm; also pass the login crediantials to verify.cfm. Once I get the return back;
I parse out the jsessionid from the cookie header and set a new jsessionid cookie on checkLogin.cfm page.
# Posted By Dharm Kumar | 1/19/10 9:57 PM
Dharm Kumar's Gravatar btw, have you seen http://www.adobe.com/support/security/bulletins/ap...

It talks about some hotfix for session fixation issues in ColdFusion. Any idea what this "fixes"?
# Posted By Dharm Kumar | 1/19/10 10:01 PM
Heath's Gravatar I am working on a project right now that requires creating a new session on login. The application has a shopping cart and returning users will most likely login on checkout, so I need to transfer all session info including the shopping cart.

My idea is to create a additional cookie on session start, that stores also stores the sessionid. When login a user into the application you could copy the session into a application variable. The sessionid will be used as the structure key. You could then force a new sessionid to be created by using <cfcookie name="jsessionid" expires="now"/>.

When the session starts you could check to see if the second cookie holding the old sessionid exists. If it does you can use this cookie to find the data in the application scope. You could then copy the old session data to the new session remove the structure from the application scope and lastly remove the cookie storing the old sessionid.

I was just wondering if this might be an acceptable way of achieving a new session on login or I am over looking something.
# Posted By Heath | 1/22/10 10:11 AM
Jason Dean's Gravatar @heath,

I do not think what you suggest is an acceptable solution. The primary reason is that you should not be sticking all of the data from the session scope into the application scope. The application scope is a scope shared by all users, and you run the risk of data leaking out where is should not or with causing yourself performance issues and locking issues.

If you want to work this way, I would say your best bet is to serialize the shopping cart and store that in the DB with a randomly generated key. Then pass that key onto the login processing code. After the login has been processed and the client receives a new session token, you can check to see if a cart key was passed, if it was, then check to see if the cart exists in the DB, if it does, reinflate it and stick it in the new session.

Hope this helps.
# Posted By Jason Dean | 1/22/10 5:45 PM
Heath's Gravatar Thanks Jason, That does sound like a much safer approach.
# Posted By Heath | 1/23/10 8:04 PM
Ben Nadel's Gravatar I know I'm super late to this party, but someone linked me to this post and I have to say that it's kind of brilliant. Especially in a computer lab at a school where there is probably a single "External" IP address and a standardized browser installed on ever machine (students usually do not have install permissions). As such, two users at different computers using the same tokens might be indistinguishable from a request-standpoint.

I didn't read all the comments, but just wanted to say the post was cool.
# Posted By Ben Nadel | 1/27/10 5:45 PM
Jason Dean's Gravatar @Ben,

Brilliant? Me? Crazy talk! :)

Yeah, this is an interesting one, and it is an important one. In routine app scans, this will get flagged as a potential problem if your application doesn't do it.

One of these days I will make the time to put together my ideas for doing this. But it is going to involve a lot of testing.
# Posted By Jason Dean | 1/27/10 6:06 PM
Ben Nadel's Gravatar Testing... meh.
# Posted By Ben Nadel | 1/28/10 9:32 PM
Chris Shannon's Gravatar Hi Jason,

This has been a great thread, I have learned a lot. However, I am having problems with keeping track of shopping cart session variables when I login in a SSL to checkout. You said that creating a record in the DB was a possibility, but is there a way we can keep track of the session (because of the shopping cart structure I am using)? It would be great to only create an order record after they click "Confirm & Pay".

If two sessions are the key, is there a way to transfer the structure information to the new secure session on login?

Thanks,
Chris
# Posted By Chris Shannon | 2/1/10 11:45 AM
Jason Dean's Gravatar @chris,

When you say you are having problems keeping track of sessions when you switch to SSL, what do you mean? When you switch to SSL are you expiring your cookie and starting a new session? Or is there some other problem?

The idea behind creating the database entry is to maintain the data from one session to the next. My thought is that it would go something like this:

1. They click checkout
2. You take their existing cart struct from the session and serialize it to text
3. You store that cart text in the DB and get back the primary key for that record (ie cartid=1234556)
4. You expire the current session cookie and redirect the user to the SSL checkout page and pass along the cartid (https://.../checkout.cfm?cartid=1234556)
5. A new session will be automatically created, so in checkout.cfm (or wherever else makes sense) take that cartid, and look up the serialized cart in the database. When you find it, deserialize it and stick it in the new session scope.

What do you think? Does that make sense?
# Posted By Jason Dean | 2/5/10 11:54 AM
Tim Heald's Gravatar Can't you simply clear the session on the log in page, then the server will create a new one on the log in action page, right?

I'm working on a medical application with a tom of PII. This is the way I'm doing it now, if it's wrong please let me know.

THX
# Posted By Tim Heald | 2/24/10 2:17 PM
Troy M's Gravatar @Tim, that won't work as the session is controlled by the CFTOKEN and CFID values stored in the cookie. You can see http://www.bennadel.com/blog/1846-Clearing-The-Ses... for an example.
# Posted By Troy M | 4/19/12 11:20 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner