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.



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.
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
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.)
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.
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.
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.
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.
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 :)
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?
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.
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.
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?
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.
This is not an option for me. So I guess I will try the 2nd option. Will let you know what I find.
Once I had the SSL certificate, it all worked like charm. I was able to verify this on FF, Chrome and IE.
Cool! And are you doing it all from within onSessionStart()?
I parse out the jsessionid from the cookie header and set a new jsessionid cookie on checkLogin.cfm page.
It talks about some hotfix for session fixation issues in ColdFusion. Any idea what this "fixes"?
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.
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.
I didn't read all the comments, but just wanted to say the post was cool.
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.
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
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?
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