Search code examples
javasession-cookiesshiro

How to safely change the session cookie DOMAIN or name in production?


We recently realized that our session cookie is being written out to the fully qualified domain name of our site, www.myapp.com, for example:

MYAPPCOOKIE: 79D5DB83..., domain: www.myapp.com

We want to switch this to being a cookie that can be shared cross subdomain, so any server at myapp.com can use the session cookie as well. For example, we'd like our cookie to store like so:

MYAPPCOOKIE: 79D5DB83..., domain: .myapp.com

We tried just changing our session cookie to use that domain like so:

   Cookie sessionCookie = sessionManager.getSessionIdCookie();
   sessionCookie.setDomain(".myapp.com");                

and this works fine in most cases.

We're finding some users can't login after deploying this new code in some situations. The problem arises when a user:

  • has been logged into our site in their current browser session, but aren't currently logged in.
  • they try and login again

It appears there are 2 session cookies in their browser:

  • a stale cookie from their previous session, with the fully qualified domain name

    MYAPPCOOKIE: 79D5DB83..., domain: www.myapp.com
    
  • the new session cookie for the session they just logged into, with the new domain setting

    MYAPPCOOKIE: 79D5DB83..., domain: .myapp.com
    

What's the best way to manage this old cookie being around? We've tried adding some code to delete the old cookie if a user doesn't have a session, but there are some paths into our app where this doesn't seem to work.

We're open to renaming the cookie if that might work, and are looking for any suggestions others may have. Thanks!


Solution

  • The following approach solves the problem by renaming the session cookie to something new, copying the old value to the new cookie name if required.

    It uses some Apache modules (mod_headers and mod_setenvif) to copy the old cookie value to the new cookie name when the new cookie doesn't already exist.

      # We only want to copy the old cookie to the new cookie name
      # when it doesnt already exist in the request. To do so, we
      # set a "per request" flag which is used to determine if we need
      # to do the "cookie copy operation".
    
      <IfModule mod_setenvif.c>
        SetEnvIf Cookie "NEW_SESSION_ID=" HAS_NEW_SESSION_ID
      </IfModule mod_setenvif.c>
    
      # If the cookie "NEW_SESSION_ID" doesnt exist in the request, copy 
      # the old cookie value to the new cookie name. This allows seamless 
      # switching to the new cookie for users with existing sessions, and
      # also ensure that if we have to rollback our code change for some 
      # reason, the old cookie will still be ok. Of course if new sessions 
      # are created with the new cookie, then they wouldn't be copied over on
      # rollback, but a similar approach could be used if someone 
      # wanted to do so
    
      <IfModule mod_headers.c>
         RequestHeader edit Cookie "OLD_SESSION_ID=([0-9a-zA-Z\-]*)" "NEW_SESSION_ID=$1 DEBUG_MSG_FOR_REPLACING=TRUE; OLD_SESSION_ID=$1" env=!HAS_NEW_SESSION_ID
      </IfModule mod_headers.c>
    

    Note that you can test if the replace is working by looking for the DEBUG_MSG_FOR_REPLACING cookie value, which I've added for debugging purposes when the replace is done.

    The following is some sample code for an endpoint that just dumps the value of the cookies header to the response, which you could use when debugging your Apache changes:

    @GET
    @Path("dump_cookies")
    public Object dumpCookies(@Context HttpServletRequest request)
    {
      String result = request.getHeader("Cookie");
      String cookies = result.replace("; ", "<br/>");
    
      String html = "<h1>Raw</h1>" + result;
    
      html += "<hr/>";
      html += "<h1>Cookies</h1>" + cookies;
    
      return html;
    }
    

    Note that we didn't end up using this solution because of business reasons that stopped us from renaming the cookie. We ended up using this solution instead which allowed us to keep our cookie name the same.