mmmmMMmmmmmmm Cookies (part 2) - Security Series #12.1

This post is a day later than I had hoped. Oh well, what can you do.

In my last post we started talking about cookie security. There is a LOT to cookie security and we only touched the surface. We are going to continue today by talking about session token cookies, SSL, and the SECURE flag.

Session Token Cookies

Session Token Cookies are used to store the browser's session token so that when the browser contacts the web server that it knows which session belongs to that browser. Because of the stateless nature of the World Wide Web, this is necessary to persist data between web requests.

To read more about session management and session tokens check out some of my previous posts.

Session Security in ColdFusion
Session Tokens in ColdFusion

Is important to know, that your users' sessions tokens are HIGHLY sensitive information. If session tokens become compromised, then session hijacking is easy for anyone that obtains a valid token. This is why it is important to make sure our session token cookies are secure.

SSL and Cookie Security

One of the problems with cookies is that everything is done "in the clear". Cookies are simple text and numeric values and these values are sent in clear text which can be easily intercepted, unless SSL is used.

When a secure connection is made between a client and a server, then all the information sent between those two machines is encrypted, including the cookie values. This, of course, is great for security, since the encrypted session token cannot be intercepted and used for a session hijacking.

Let me just give you a quick example to try to scare you. You have a client with a very simple request. She wants a blog. You say "No Problem!", because, of course, thanks to our awesome community, there are several blog options from which to choose. You choose one and set it up. You think to yourself, "Does this site need SSL?", and decide it does not. It's just a simple blog, the only users, besides the administrator that will be accessing it, are all anonymous and so you don't need to worry about having those users' sessions hijacked.

But wait. What about your administrator? Your blogger? Isn't she a user? What happens when she sits down at the local coffee shop with her laptop and hits that wireless access point called "Free Public Wifi" and goes to log into her blogging site. Well, because she is not connected via SSL, everyone one of her page requests will be broadcasting her highly sensitive session token cookie "in the clear". If some malicious little punk was sitting there, monitoring network traffic, and saw that, he could easily grab that cookie, duplicate it on his machine, and browse to the blogging site himself. He would be instantly logged on as your client and could vandalize to his heart's content.

A few hours later you would get a call from an angry client wanting to know how the site you set up for her got hacked. Your reputation takes a hit and you get to clean it up for free.

The moral of this story is a simple one. It is also one that nobody wants to hear. But I don't care what you want to hear, I just want to give it to you straight.

If you are using session management and not using SSL, then your site is not secure. Period.

The SECURE flag

Cookies have a pretty cool feature that can help protect you from yourself and from your clients. It is the SECURE flag. When a cookie has the SECURE flag set to "true", then the cookie will not be sent to the server on anything except an encrypted connection. Pretty cool, huh?

So how do we set the SECURE Flag?

Great question. Unfortunately, this is one of those areas where ColdFusion drops the ball a bit. There are several ways to protect your session token cookies that ColdFusion does not allow you to do without jumping through a few hoops. This is one of them. There is no way (to my knowledge) to tell ColdFusion to write session tokens with the SECURE flag set to "true". So we need to write the cookies ourselves.

The first thing we need to do is tell ColdFusion NOT to write the session cookies. We do this in our Application.cfc (or Application.cfm if you are kickin' it oldSkool).

In the pseudo-constructor area of Application.cfc (which is the area right below the <cfcomponent> tag and right before the first function) put:


<cfset this.setClientCookies = false />

In Application.cfm add the setClientCookies attribute to the <cfapplication> tag and set it to "false".

This will tell ColdFusion not to write the session token cookies.

Next we need to write the cookies. So in Application.cfc in your onSessionStart() method, or in Application.cfm where ever you handle your session variables, add:


<cfheader name="Set-Cookie" value="CFID=#session.CFID#;secure=true;" />
<cfheader name="Set-Cookie" value="CFTOKEN=#session.CFTOKEN#;secure=true;" />

I have tried this with jsessionid and it does not seem to work. If anyone has an experience with making the jsessionid token cookie secure, I would love to learn about it.

Conclusion

This is important stuff, please do not take it for granted. Cookies are important for proper session management, and you do not want to open yourself up for attacks. In future posts we are going to look at when you should be invalidation sessions and sesion token cookies and why. We will also be looking at making CFID and CFToken non-persistant and at making CFToken a non-stupid value.

Comments
JAlpino's Gravatar I wrote up a small piece about 'securing' cookies on my blog, where I take a slightly different approach in that I re-write/overwrite the existing cookies. That technique has worked for me both with CFID and CFToken but also with jSession id. My post doesn't mention where the best place would be to implement the code but at my office we did so under Application.cfm (yup, were kicking it old school is some areas) wrapped in a condition of "if not session.securedcookies....secure cookies".

http://www.jalpino.com/new/index.cfm/event/read/en...
# Posted By JAlpino | 1/8/09 11:58 AM
Jason Dean's Gravatar @jalpino,

Thanks for the comment. I saw your post when I was doing my research and testing. I forgot to mention it, but there is a reason I chose to use <cfheader> instead of <cfcookie> and that reason is the HTTPOnly flag cannot be set with <cfcookie>. So by using cfheader, you can set both SECURE and HTTPOnly.

It is interesting that you can use cfcookie for jsessionid but that cheader would not work. I will need to look into that.
# Posted By Jason Dean | 1/8/09 12:16 PM
JAlpino's Gravatar @jason - good call on using <cfheader>, until I read about it in your previous post, I hadn't considered that approach. I wonder if it would be possible to combine using cfheader for HTTPOnly, and cfcookie for securing.. definitely something to experiment with.

BTW, nice series, very informative and relevant. I'm noticing that many of my company's clients are becoming more aware of the security aspects of applications they use, on a much deeper level than they inquired about before in the past. Over the past year or so, we've been subject to several large security questionnaires and requests for independent application scans. The information that you are posting is great and definitely helpful!
# Posted By JAlpino | 1/8/09 4:38 PM
Jason Dean's Gravatar @jalpino - Thanks for the kind words. What you describe is exactly my reasons for these posts. I want to help others become aware of the threats, but also to become aware of what will be expected of them.

Again, thanks for reading. It's comments like these that inspire me to keep going.
# Posted By Jason Dean | 1/8/09 7:48 PM
Eric Hoffman's Gravatar AHA! Had not thought about using cfheader.

Invaluable. Thank you.

Glad to see another MN developer leading the secure development charge!
# Posted By Eric Hoffman | 1/9/09 9:00 AM
Chris Luksha's Gravatar Hi Jason - I have really enjoyed my last couple hours reading this entire series from 1-12 and everything in between. Thank you. The last few posts I recognized from your recent talk at cfmeetup so it was nice to rehash some things and give my brain a minor break.

I was wondering a couple of things.... In your code - is this code correct? <code>
<cfheader name="Set-Cookie" value="CFID=#session.CFIF#;secure=true;" />
<cfheader name="Set-Cookie" value="CFTOKEN=#session.CFTOKEN#;secure=true;" />      
Should the session.CFIF be CFID?

Either way: Why are you setting the cfid and cftoken cookie to the exact same values as the session.cfid and cftoken? I thought we were trying to get around the easily guessed values in those default values.

Also - Why are you setting the secure=true in the application or session settings? Most user auth systems don't actually secure themselves until you attempt to login in somewhere - so the user is often well beyond the session Start or the Application Start at that point. How do we know for sure the user is using ssl when they start up?

I hope I am not being too confusing to you b/c I am throwing myself for a huge loop.

thanks,
Chris
# Posted By Chris Luksha | 1/26/09 2:46 PM
Jason Dean's Gravatar @Chris, thanks for the comment.

Yes, I had CFIF where I wanted CFID. That is now fixed. Thanks.

Now, I am setting the cookies to the same value that is in the session scope because that is what ColdFusion is expecting to use to find that user's session data, if I try to change it to anything else, then I will lose the session.

The way to get rid of the weak value for CFToken is to check the "Use UUID for cftoken " setting in the ColdFusion administrator. This will stop ColdFusion from using stupid 8 digit numbers for the CFToken and use a 35 character UUID instead. Nice. There is nothing we can do about the CFID. It is fine the way it is. As long as the CFToken is hard to guess.

You last question is a good one. In my example, the assumption I am making is that the application is on SSL from the very beginning and that any attempt to browse to the site with HTTPS will result in a re-routing.

If, as you suggest, your site does not switch to SSL until a login, then you would want to change your cookies at that point instead of in the onSessionStart() method. Technically, you would want to invalidate that old session completely and start a new one with the SECURE setting, but that is a future post that I will explain in more detail later. Great observation. Thanks!
# Posted By Jason Dean | 1/26/09 3:43 PM
Chris Luksha's Gravatar Do you know if the Use UUID for CFToken requires a reboot? I made that setting in my local dev machine and reset the applicaiton and session vars using timeout=0,0,0,0 and still I see the crappy old cfid and CFToken numbers.

Now that I am re-reading the post and the comments I am not sure about this: Why is it that we are setting these cookies at all if we have turned setClientCookies to false? Client cookies are off - so won't these cookies not even get used? I think I am still really misguided as to the diff between session tokens and session cookies.
# Posted By Chris Luksha | 1/26/09 6:37 PM
Chris Luksha's Gravatar And oh yeah - thanks again. I really appreciate the help.
# Posted By Chris Luksha | 1/26/09 6:38 PM
Jason Dean's Gravatar @Chris We are changing setClientCookies to false so that we can control the cookies. The cookies will still be used, we are just asking ColdFusion not to set the cookies automatically. That way we can set them the way we want to (secure, HTTPOnly, etc.)

As for why yours is not using UUID cookies, I am not sure. It should not require restarting ColdFusion. But try it anyway. Also, delete any existing cookies you have. If that does not work, email me at jason@12robots.com and we'll set up a connect session and try to figure it out.
# Posted By Jason Dean | 1/26/09 7:36 PM
masterorb's Gravatar @ Jason , @ JAlpino

First of all, thanks guys. Secondly, I think I have synthesized your strategies to provide a solution for JSESSIONID.

1. In Application.cfm (yes, oldSkool), I specified setclientcookies="false" to ensure that CFID and CFTOKEN were nowhere to be seen.

2. Along with JSESSIONID, I get a client-side cookie var called CFAUTHORIZATION_myApp. I'm not sure what this is (any help?), but it seems to go along with JSESSIONID. It needs to be preserved, as JSESSIONID does, to keep the session going. So I treat is as JSESSIONID

3. Here's the code:


<!--- save current JSESSIONID and CFAUTH locally --->
<cfset currJSessionID = cookie.JSESSIONID>
<cfset currCFAuthorization = cookie.CFAUTHORIZATION_myApp>
   
<!--- clear cookies --->
<cfset tmp = StructClear(cookie)>

<!--- redeclare cookies w/cfheader tag so that we can set them as HTTPOnly -->
<cfheader name="Set-Cookie" value="JSESSIONID=;domain=#CGI.SERVER_NAME#;path=/;HTTPOnly" />
<cfheader name="Set-Cookie" value="CFAUTHORIZATION_myApp=;domain=#CGI.SERVER_NAME#;path=/;HTTPOnly" />
   
<!--- assign stored values with cfcookie tag so that we can set security --->
<cfcookie name="JSESSIONID" value="#currJSessionID#" secure="true">
<cfcookie name="CFAUTHORIZATION_myApp" value="#currCFAuthorization#" secure="true">


Admittedly you could code this a little tighter but it's 4:30am. Also note it's a dev server so domain is set to an IP.



The key thing turned out to be the path. If you do not assign "path=/" explicitly, ColdFusion assumes that you are declaring a new cookie that just happens to be *named* JSESSIONID instead of a JSESSIONID that will hold sessions across your application.

Also, my site doesn't have SSL, so when I do the above code, it kicks me off immediately. I have to set secure="false" to work with my app. But this fits with what JAlpino said about needing SSL to handle secure cookies, so I am comfortable with the code until I get my SSL.


I want to thank you guys personally for helping me with this!

(I am a security n00b, my main job in life is author of young adult novels, I do this for money on the side)
# Posted By masterorb | 4/7/09 2:39 AM
masterorb's Gravatar ug sorry the last part of that comment made me sound like a schmuck, i also do this because it is effing cool
# Posted By masterorb | 4/7/09 2:49 AM
Jason Dean's Gravatar @masterorb,

So I have tried your code in every way I can think of, and consistently, I get the same result that I did with any of my methods. When I try to set the JSESSSIONID cookie, with DOMAIN, PATH, or HTTPOnly, I either get a duplicate cookie, or it just doesn't work.

The <cfcookie> tag does work to make the jsessionid SECURE, but it is not HTTPOnly from the <cfheader>

The other issue I come across is that ColdFusion (or maybe it is JRun) will not let you create a J2EE session cookie with any path other than "/". Once you try to set a path to any kind of sub-directory, it creates a duplicate cookie.

Please let me know if you actually are getting different results. But as far as I know, if you use a <cfheader> to right a cookie, and then follow it with a <cfcookie>, the <cfcookie> will overwrite the cookie, not append information to it.
# Posted By Jason Dean | 4/7/09 8:56 AM
Ned's Gravatar Shoot. How can I tell if a cookie is HTTPOnly once it has been set? Don't see it via Firefox Options --> Privacy
# Posted By Ned | 4/7/09 9:02 AM
Jason Dean's Gravatar I use Firecookie: http://tinyurl.com/3dd78b
# Posted By Jason Dean | 4/7/09 9:18 AM
masterorb's Gravatar Yes, using that tool I see that that "solution" is completely useless. I'm sorry. So if we can't set both secure and HTTPOnly in a custom replacement JSESSIONID, can we set just HTTPOnly?

Thanks!
# Posted By masterorb | 4/19/09 8:59 AM
masterorb's Gravatar Huh, when I use this code:

<cfif IsDefined("cookie.JSESSIONID")>
   <cfheader name="Set-Cookie" value="JSESSIONID=#cookie.JSESSIONID#;domain=#CGI.SERVER_NAME#;path=/;HTTPOnly" />
</cfif>


Firebug says that my JSESSIONID is HttpOnly all the way through--*except* when I log out and clear my cookies. Thoughts?
# Posted By masterorb | 4/29/09 12:22 AM
Mike's Gravatar @masterorb
the CFAUTHORIZATION_myApp cookie is set by the <cfloginuser> tag. It tracks user authentication.
You can get rid of it by specifying a session storage for the login (just set the "LoginStorage" application variable to "session").
I switched to session storage because I had issues with session timeout: the session was timing out before the cfauthorization cookie, leading to odd behaviours (the user being considered logged in but with an empty session).

I'm also trying to secure the jsessionid, I would like to specify a path more specific than / (many applications being behind a proxy at my job, they are overwriting each others sessions and leaving too much place for a malicious application). Let me know if you found anything on this...

Thanks for this interesting blog!
# Posted By Mike | 5/6/09 8:17 AM
masterorb's Gravatar Your CFAUTHORIZATION strategy is exactly what I used. I have officially abandoned trying to make JSESSIONID HTTPOnly--the best and brightest in the ColdFusion world seem to think it just can't happen. So I guess a future version of CF will handle it.

As to making JSESSIONID "secure", I don't know if that is possible yet (jalpino definitely has a solution for making CFID and CFTOKEN secure) but I haven't implemented SSL yet, I am still developing.

Thanks!
# Posted By masterorb | 5/6/09 8:38 AM
Jason Dean's Gravatar OK guys, I've looked more into this, and come up with a possible solution for SECURE, HTTPOnly and PATH for JSESSIONID token cookies.

http://tinyurl.com/12RobotsjSessionCookies
# Posted By Jason Dean | 5/6/09 2:34 PM
Michael Burk's Gravatar It seems like cfcookie should really be how this is handled. It's explicit. It doesn't require a workaround for redirection afterward. And, I've also run into a problem settnig an AES encrypted value for a cookie using cfheader where the same value worked in cfcookie.

I'd expect HTTPOnly to be an option for the cfcookie tag in CF9 or, at least, to be added by the OpenCFML advisory committee. Until then, there's one other option I haven't seen mentioned. Understandably so, but still. Most of the major java servlet engines now have the option to force cookies to be HTTPOnly by default. Tomcat, Resin, and Jetty all have this and it's usually a case of adding one line to the XML config file at the context level. I know many people are on JRun or are hosted and might not have that type of control, but for those of us running our own server or with flexible hosts, it's something to consider.
# Posted By Michael Burk | 6/29/09 1:33 AM
Jason Dean's Gravatar @Michael,

Thanks for the comment. I just have a few comments/questions.

1. I'm not sure where you are seeing a "workaround for redirection" in any of the suggested solutions that use cfheader.

2. If you are setting an encrypted value to a cookie, it should be encoded as a string (BASE64 or Hex or something), I don't see how that could fail where any other string would work.

3. I'm not sure what to expect for CF9 as far as cfcookie is concerned, but there are plenty of people that will not be immediately upgrading that this still merits research and concern.

4. I'd love to see a HOW-TO on setting HTTPOnly or SECURE flags for J2EE session tokens in any of the servlet containers. I'll admit that I have not looked very hard for such docs, but if you know of any, please link me.

Again, thanks for the comments.
# Posted By Jason Dean | 6/29/09 10:12 AM
masterorb's Gravatar re: #4 never did get it to work, still had a JSESSIONID running around that CF created that wasn't secure or HTTPOnly.
# Posted By masterorb | 6/29/09 10:55 AM
Michael Burk's Gravatar 1. I just dislike anything that's not explicit. To me, that's a core feature of CFML. Opening up generic tunnels like cfheader and passing "cryptic" values suggests there's nothing in the language to accomodate it....hence it feels like a workaround for a missing feature.

2. Absolutely correct. Base64 encoding resolved it.

3. Fair enough. Still might be useful for those who can take advantage of it.

4. Tomcat
http://stackoverflow.com/questions/33412/how-do-yo...

Resin
http://ccl.pku.edu.cn:8080/resin-doc/doc/webapp-ta...

I've got Tomcat runnning with httponly session cookies now.
# Posted By Michael Burk | 7/20/09 2:11 PM
Jason Dean's Gravatar @Michael,

Thanks for following up.

1. I know it seems kludgey, but I am not sure there is a better solution. Rotating session ids required a round trip HTTP request to get and set the new cookies. So I am not sure of a better way. Can you point to an example of a way another language does it? That might help us figure out a better way or suggest a better way to Adobe.

2. Glad to hear it.
4. Thanks for the links. I am going to try that out. I got some info from someone else I am going to try out too.
# Posted By Jason Dean | 7/21/09 3:40 PM
jalpino's Gravatar I'm sure that you guys have already looked into it, but it does not look like any enhancements were made to cfcookie in the new release. Of course its still in beta, but I'm doubtful we will get 'httpOnly' attribute would be added before final release.

@micheal thanks for posting the links on enabling httpOnly at the server level for resin and tomcat. I looked for info on configuring jrun but could not find any. I wonder if "standard" installs of CF are left to programmatic solutions.
# Posted By jalpino | 7/21/09 8:40 PM
Joe's Gravatar You can set a secure-attribute JSESSIONID cookie (meaning the cookie is only sent when using SSL) by adding the following line to C:\JRun4\servers\yourservername\cfusion-ear\cfusion-war\WEB-INF\jrun-web.xml.

Put this after the </persistence-config> element.

<cookie-config>
<cookie-secure>true</cookie-secure>
</cookie-config>

This answer cost us $500 after speaking to Adobe Tech Support. It turns out that this is documented on http://livedocs.adobe.com/jrun/4/Programmers_Guide...

This page also has a lot of other useful JRun info.
# Posted By Joe | 12/22/09 3:37 PM
Jason Dean's Gravatar @joe, thanks for the comment. You're right. I'm sorry to hear that you paid for the answer. Someone else mentioned it in the comments of my other post here:

http://www.12robots.com/index.cfm/2009/5/6/Making-...

That solution will probably work for a lot of people, but unfortunately, it cannot work for everyone. That solution will only work in an application that is 100% SSL.

Many applications will only switch over to SSL when data needs to be protected, but then switch back to standard HTTP for routine stuff.

If the 100% solution works for you, then that is awesome. It actually works for my applications too. But I know it doesn't work for a lot of developers, which is why I am trying to find a better solution for them.
# Posted By Jason Dean | 12/22/09 4:01 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner