Sessions don't work in Chrome but do in IE
I observed an interesting thing today while helping a client. The problem was presented as:
We have a bunch of Chrome users having issues where a session variable is not working between page requests. We set the variable on one page, it is not defined on the next page request. If we tell them to try another browser, such as IE or Edge it works fine.
I have seen problems like this for many years and it doesn't often end up being a difference in the browser implementation, but rather the difference is that the user has essentially cleared their cookies when they try another browser. In this case the client confirmed that the problem went away when they use a new Incognito Chrome browser window.
Attacking the problem
Next I fired open Chrome developer tools to see what was happening. This particular website was serving requests over both
http (port 80) and
https (port 443). The session cookie (
JSESSIONID in this case) was getting the
secure flag set when the request came over
https, a request over
http would not get the
secure flag set.
secure flag is set by a cookie the browser must not send that cookie over an insecure transport like plain
http. This is the essence of what a secure cookie does.
Even though a request over
http would cause Tomcat or ColdFusion to set a new
JSESSIONID cookie, Chrome will not store that cookie if it already has a cookie with the same name and the secure flag is set.
So here is what happened:
- User requests https page, sets
JSESSIONIDwith secure flag.
- User makes request to non-secure http page, the JSESSIONID is not sent because it is a secure cookie. The server creates a new session, and tries to set a new JSESSIONID cookie, but the browser ignores this new cookie because it already has a JSESSIONID.
- User navigates to another page over non-secure http they do not have a session cookie to pass, repeat step 2.
Do all browsers work this way?
Both Chrome 71, and Firefox 64 prevent a secure cookie from being overwritten on plain http. I tested Edge 42 and it does not work the same way, it will overwrite the secure cookie with the non-secure cookie. Safari 12 works similar to Edge.
My assumption was that the browser would maintain separate cookie values for the secure origin and the non-secure origin in this case. My assumption was not correct on any modern browser. I tested a few old versions of Chrome and found that this implementation was added somewhere between Chrome 37 and Chrome 60 (I didn't want to test each version, I will leave that as an exercise for the reader ;-).
Here's an easy way to test a browser using httpbin.org a site/api for testing HTTP clients, it is useful because it serves requests over both plain http and https. Additionally their set cookie feature will set a cookie with a secure flag when requested over https:
- Set a secure cookie
test_secure=1over https: https://httpbin.org/cookies/set/test_secure/1
- Next set a non-secure cookie
test_secure=2over plain http: http://httpbin.org/cookies/set/test_secure/2
If you see test_secure=2 over http then the browser is overwriting the secure cookie, otherwise you will not see the test_secure cookie over http.
How do you work around this?
Fixing this is pretty straight forward, you have to redirect all http traffic to https and the problem is solved. There is not really any valid use cases for serving content on both http and https that I am aware of.
- Cookie Expires / Max-Age 1969-12-31T23:59:59.000Z - January 24, 2019
- Firefox Aurora now Supports Content Security Policy 1.0 - May 31, 2013
- J2EE Sessions in CF10 Uses Secure Cookies - April 5, 2013
- Firefox Now Supports HttpOnly Cookies - July 19, 2007
- AJAX on IE - back to the IFRAME - August 17, 2005
- Updating Java on ColdFusion or Lucee
- ColdFusion returning empty response with server-error: true
- Careful applying CF11u16, CF2016u8, CF2018u2
- csrfVerifyToken does not invalidate the token
- The cf_sql_ is optional in cfqueryparam
- Cookie Expires / Max-Age 1969-12-31T23:59:59.000Z
- Burst Throttling on AWS API Gateway Explained