Lots of sites appear to support https but don't use secure cookies. I want to make my site use secure cookies but to allow for some content to be accessed using http instead.
A sensible way to do this appears to be to have a secure cookie for the real session, and a non-secure cookie which is just a flag to say if the user is logged in or not (to display different things in the header, like a logout link instead of a login link). This cookie wouldn't contain any "real" session information and is just so that the site can show pages slightly differently for logged-in users compared to logged-out ones on http portions of the site.
Having the whole site as https is another option but this appears to be quite a bit slower than plain http and so is not really ideal.
Why don't sites use this kind of set-up and have secure cookies? The possibility of cookie theft seems to make secure cookies a necessity nowadays. Is there a better way to achieve the same thing?
The solution you propose seems like it would work, as long as you don't mind non-authorized people being able to view the non-secure (http) part of the site 'as if they are logged in' - ie as long as the http part of the site does not contain any sensitive information, and the only difference between logged in and not-logged-in users is something harmless in the header.
The reason it is not used very often may be one of:
- This scenario may just not be very common. Usually if you care enough to make part of your site secure, you'd restrict the login session just to that secure part, or you'd make the entire site always use HTTPS (like Paypal).
- Pre-existing solutions exist which are secure and which are capable of more than this, for example logging in someone at an HTTPS login form and maintaining that session while transferring them back to HTTP.
OpenID's an example. Also think flickr or gmail: their sign in page is always HTTPS, but once the session's started you migrate back to HTTP while maintaining the session securely.
Update (Aug 2014)
Since I wrote this back in 2009, the practice of having a secure connection for the login screen but dropping back to HTTP once logged in has all but disappeared.
The overhead of using HTTPS side-wide is not seen as much of a big deal anymore. The new SPDY protocol pioneered by Google (now evolved into HTTP/2) is supported cross-browser and by major web servers and improves HTTPS speed.
And lastly, privacy is seen as more important than ever, even for actions that aren't critical to the authentication, such as writing comments, uploading photos, and more.
Google has even said recently that sites which are HTTPS-only will start to benefit in search engine rankings.
From a security standpoint, you should never trust any content sent over a non-secured connection. So with that in mind, then it is safe to use a cookie sent over an unencrypted connection only if the cost of theft or misuse of that cookie is approximately zero.
With that in mind, most sites are designed such that the data isn't allowed to "leak" between the channels. After all, data coming from the encrypted side is usually privileged, and therefore shouldn't be allowed in the normal channel, while data coming from the unencrypted channel is potentially spoofed, and shouldn't be trusted.
If you have data that doesn't fit those generalizations, then feel free to do with it as you please.
Transferring session cookies over HTTP has been bothering me for a while. I think the technique you described is the only sane way to secure cookies while making it possible for logged in users to browse HTTP pages as if being logged in. However, I've rarely seen this implemented.
Why don't sites use this kind of set-up and have secure cookies?
I think the main reason for lack of adoption is risk management:
- Stealing session tokens via eavesdropping is much harder than e.g. cross-site scripting (assuming there is a vulnerability). You need access to the network (e.g. user's LAN or ISP). Thus, according to risk-based prioritization developers should tackle XSS issues first because it provides a much bigger attack surface (the probability of an attack is much higher).
- The same is true for CSRF and UI redressing (aka click-jacking).
- If the business impact of sessions being hacked is high (e.g. storing credit cards for later use in a web shop), you might be better off restricting your whole site to HTTPS.
Another reason can be usability concerns: With your proposed scheme you're effectively managing two concurrent sessions for a single user. This is easy enough as long as the logged-in-flag is the only state stored in the insecure session. If you can also change settings like language and country from within both sessions it can get messy (to implement or use).
Is there a better way to achieve the same thing?
From The Web Application Hacker's Handbook:
If HTTP cookies are being used to transmit tokens, these should be flagged as secure
to prevent the user's browser from ever transmitting them over HTTP. If feasible, HTTPS should be used for every page of the application, including static content such as help pages, images, and so on.
Seriously, make the whole site use HTTPS. A few years back this might not have been feasible mainly because of CDNs not providing HTTPS support. However, today it's mainly a question of balancing development and operational costs.
I'm fully aware that the recommended practice is to just force SSL on the entire site. However, there are certainly unique cases where being able to pick and choose between HTTP and HTTPS could come in handy.
I was running into a similar scenario as @Dsavid Gardner. My company uses a third party vendor to manage our store portion of our site, and that store resides on the subdomain "https://store.mysite.com". We have 15 years worth of video content, and our current video management vendor breaks when a video is embedded in an SSL. (I'm guessing it's pulling in resources from HTTP domains, but that's another problem for another day)
Sure, I could purchase an SSL and go through the process of debugging two, third-party vendors, as well as doing a search and replace on our entire database (or an .htaccess file hack, but I digress) to correct any HTTP resource links, just to be able to have a message in the header say "Welcome 'YourName'", but that just seems like overkill.
Here's a simple Javascript solution that I came up with that sets a site-wide, insecure cookie based off of the secure cookies that are already set.
First, I grabbed some javascript cookie functions. Go ahead and put this code in the secure portion of your site:
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)===' ') {
c = c.substring(1,c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length,c.length);
}
}
return null;
}
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+d.toUTCString();
/* Note, the W3 documents where I got this code didn't include the
option to set the domain. I added this and it allows the cookie
to be shared across sub-domains. Be sure not to add "www" */
document.cookie = cname + "=" + cvalue + "; " + expires + "; domain=.yourdomain.com";
}
/*Now we check our cookies on our secure server to find out if the user is
logged in or not. In my case, the First Name is stored as a cookie. */
var firstNameCookie = readCookie("the-secure-cookie-name");
//
if(!firstNameCookie){
/* If the cookie doesn't exist, then the person isn't logged in. Add
conditional logic here if you'd like (such as deleting any current
logged in cookies from the HTTP portion of the site) */
}
else {
/* otherwise, we have a successful login. By grabbing the cookie via
this javascript resting on the secure server, we haven't compromised our
security. However, if we set a cookie with javascript right now, it
won't be a secure cookie by default and we'll have access to it with
HTTP on the subdomain */
setCookie("HTTPfirstName", firstNameCookie, 1.5);
}
*/The clients first name is now accessible across subdomains in the cookie
entitled "HTTPfirstName" */
In this instance, the only thing we've leaked over to our HTTP server is the client's first name. However, if you would like even more security, you could set your server settings to only allow certain cookies (i.e. "firstNameCookie) to be accessed by an HTTP request, and that adds an extra layer of protection. You can learn how to do that here
Sure, this isn't the most ideal solution. In the future, I plan to implement SSL site-wide, but having a simple javascript function to replace it in the meantime is sure nice to have.