Many of you probably know that I sell UI Flex Components under the name of Flextras. Part of the decision I made when starting the company is to open up a merchant account and process credit cards on the site, instead of using a third party site such as PayPal. This gives us complete control over the branding experience.
As part of this decision to touch credit card data, the site is subject to routine PCI Compliance scans, and the scan often makes suggestions for items that must change on the site in order to stay compliant. I think the penalty for not changing this stuff is immediate death.
The latest scan failed me for a few issues that have been on the site since it launched. If it was fine for the first 18 months, why do I have to spend and money changing it now? But, I digress. I wanted to write about one of the things they are making me change this time around
The two issues are these:
- Cookies set by the site are not set to HTTPOnly cookies.
- When cookies are served over HTTPS they are not specified as secure cookies.
The scan information was very light, so I'm not sure which cookies they are referring to. The site's code doesn't set any cookies, however there are some tracking cookies set by analytic software and session tracking cookies set by ColdFusion.
So, the question is, how do I Set the ColdFusion session cookies (CFID and CFToken) to be HTTPOnly and Secure? A quick google search brings up these two Stack Overflow questions, which lead to this post by Jason Dean. Thank you Jason!
Jason explains how to modify the JSessionID to set the HTTPOnly and Secure properties on the cookie. I was easily able to encapsulate Jason's code into a method, which I put in Application.cfc:
2<!---
3Do some cookie Magic to make cookies be HTTPOnly
4http://www.12robots.com/index.cfm/2009/5/6/Making-the-JSESSIONID-Session-Token-Cookie-SECURE-and-HTTPOnly-and-settings-its-PATH
5--->
6
7<!--- Expire the old Cookie --->
8<cfcookie name="cfid" expires="now"/>
9<cfcookie name="cftoken" expires="now"/>
10
11<!--- Get the HTTP Response Object --->
12<cfset response = getPageContext().getResponse() />
13
14<!--- Set the specifics for the cookie --->
15<cfset path = "/" />
16<cfset domain = 'www.flextras.com' />
17
18<cfif cgi.HTTPS is "on">
19 <cfset secure = "Secure" /> <!--- Use val of "Secure" or leave blank --->
20<cfelse>
21 <cfset secure = "" /> <!--- Use val of "Secure" or leave blank --->
22</cfif>
23
24<cfset HTTPOnly = "HTTPOnly" /> <!--- Use val of "HTTPOnly" or leave blank --->
25
26<cfscript>
27 header = "cfid" & "=" & session.cfid & ";domain=." & domain & ";path=" & path & ";" & secure & ";" & HTTPOnly;
28 response.addHeader("Set-Cookie", header);
29 header = "cftoken" & "=" & session.cftoken & ";domain=." & domain & ";path=" & path & ";" & secure & ";" & HTTPOnly;
30 response.addHeader("Set-Cookie", header);
31</cfscript>
32
33</cffunction>
Basically, the code expires the current cookies, and re-creates then using the same values. Along the way it specifies the HTTPOnly and Secure header. I call this method in OnSessionStart of the Application.cfc:
HTTPOnly works great. But, Secure has given me a bit of a head scratcher. If you set the secure value on a cookie, without using HTTPS the session is created new each time which defeats the purpose.
Ideally, I want to turn the secure setting on and off as people switch from secure to non-secure portions of the site, keeping the same same session users surf the site. ( This doesn't quite work as I had hoped, but more on that later ).
I create a session variable in onSessionStart, named previousURLSecure:
This defaults the value. Now, in OnRequestStart I can previousURLSecure against cgi.https . If they have changed, that means that the site surfers have moved from secure to non-secure, or non-secure to secure and we need to swap the secure attribute of the cookie. This is the code in onRequestStart:
2 <cfset this.recreateSessionCookies()>
3</cfif>
4<cfset session.previousURLSecure = cgi.HTTPS>
It's a quick check, and if things have changed it calls the recreateSessionCookies method. It also saves the HTTPS state of the current URL.
This code is live on the Flextras site right now. So, go over and add something to your cart (non-secure), then sign in (secure). You should see the item in your cart.
The go to the address bar of your browser and change 'http' to 'https'. The items in your cart will vanish and your login go away. This does not happen if you never use the secure attribute when creating, or modifying, the cookies.
So my conclusion is that you can easily go from non-secure to secure cookies, but you can't go the reverse. If you use non-secure cookies they'll work for both HTTP and HTTPS. But, secure cookies are not accessible via HTTP. Am I right?
I'm thinking this may be expected behavior for security purposes to prevent some form of session hijacking or other hack attacks. IS it?
I thought of a few hacks, such as storing a copy of the session ID in an alternate cookie, and using that to reset CFID/CFToken. But, I doubt that will be acceptable from a PCI Compliance standpoint.
I'm worried that not even this approach will be acceptable to the PCI Compliance scanner. If not, I'll have to move the whole site to HTTPS; which may suck, especially for file downloads.
So, has anyone out there had any experience with this sort of thing? Got any suggestions for me?
If you can help, I have some "Friend and Family" discount passes to Flex Camp Wall Street coming up next week.
#1 by Sam Farmer on 11/10/10 - 1:57 PM
#2 by Jeffry Houser on 11/10/10 - 2:33 PM
I guess those attributes would make things a bit easier in terms of code I had to write. But, wouldn't solve the issue of preserving the session when switching between https and http.
#3 by JAlpino on 11/10/10 - 4:47 PM
<cfset bridgeIndentifiers = encrypt("cfid=#session.cfid#&cftoken=#session.cftoken#", yourkey, yourAlgo) >
...
<a href="http://yourdomain.com/index.cfm?_bridge=#bridgeInd... to non-secure page</a>
Then in onRequestStart method in your Application.cfc, look for the "_bridge" query string param, decrypt and use those values to re-write the session cookies.
I can't say that I've personally used this approach (I haven't had the need) but understanding what the issue is, I think it could work and get you to "pass" the compliance scan...
#4 by Jeffry Houser on 11/16/10 - 10:13 PM
I like that idea a lot. I'll have to see if it applies. I don't think there is any place on the site where we explicitly link from secure back to non-secure. I guess it is more of a theoretical thing I noticed.
#5 by Floyd on 11/24/10 - 11:27 AM
You misunderstand the purpose of secure cookies, which is to never let their value be transmitted plaintext.
So by "upgrading" a non-secure cookie to secure you are undermining the purpose of the secure flag, since the cookie value has, until this point, been transmitted plaintext.
The wikipedia article on cookies in my opinion sufficiently details the intended use of secure cookies. From what I read here you are misusing them and are opening the door for your site's visitors to have their secure sessions hijacked.
#6 by Jeffry Houser on 11/30/10 - 3:08 PM
That seems like a pretty valid concern. I'll probably just end up pushing the whole site to HTTPS except for downloads which I'll revert back to HTTP.