[NOTE: This post corrects my errors in phrasing the security vulnerability in the use of Secure Pages and Secure Pages Hijack Prevention modules.]
Mixed sessions refers to providing, with respect to a user visiting a site, some content over an insecure (HTTP) connection and other content over a secure (HTTPS) connection. A quick online search reveals the widespread problem of supporting mixed sessions. For example, simply redirecting to a secure connection does not eliminate the possibility of session hijacking. To avoid the issues, some sites have gone to all HTTPS sessions (e.g. github). Others have implemented a mixed environment with varying degrees of success. In the Drupal world, Drupal 7 does a better job of addressing these concerns than earlier versions. If you have a Drupal 6 site that wants to implement mixed sessions, what are your options?
I have a Drupal 6 client situation involving an online shop that allows you to buy products without having to register for an account. The usage preferences are for:
- visitors to shop (i.e. browse products and add products to the cart) in insecure mode
- visitors to switch to secure mode for checkout
- site administrators to access the backend in secure mode
To complicate matters, the client:
- sponsors three commerce sites with shared product content (using the domain module)
- provides shopping cart features on two of the three sites
From a technical standpoint, we want to:
- retain session data when switching between security modes (i.e. between HTTP and HTTPS connections)
- avoid session hijacking
Several modules are available for Drupal 6 that make it easy to switch between security modes. These include:
These modules provide various configuration settings to indicate which pages to serve up over HTTP or HTTPS. The Secure Pages and UC SSL modules simply redirect to secure mode, but provides no protection against session hijacking. The Secure Pages Hijack Prevention module attempts to address the problem of session hijacking with Secure Pages (by creating a second session cookie with the "secure" flag set so it is only transmitted to SSL-protected pages). However, this cookie is only created for authenticated users and only checked while logged in. (In its README file, the Hijack Prevention module frowns on enabling the PHP "session.cookie_secure" setting as this "would defeat the purpose of this module." This seems to go against best practices and is contrary to the approach under Drupal 7 and 8.) Because these modules use a single session name, there is only one record for a user in the sessions table. Thus, session data is not lost on a step up to secure mode. However, the protection is incomplete for any user with Secure Pages alone and also for an anonymous user even with the companion Hijack Prevention module.
The 443 Session module attempts to improve on the Secure Pages combination, including utilizing the PHP "session.cookie_secure" setting and providing better support for anonymous users. However, enabling the "session.cookie_secure" setting has an unhelpful side effect. When a request comes in using HTTPS, Drupal core creates a second session record for the user in the "sessions" table of the database. This second session knows nothing about the first session. For example, if there is "data" associated with the first session, this is unknown to the secure session. During the switch (or "step up") from insecure to secure mode, this module (in conjunction with Drupal core) does not preserve the session data. Thus, the visitors to your site can add products to their cart over HTTP sessions, but their cart information is lost when they go to checkout using the new HTTPS session. They are greeted with a message like "There are no products in your shopping cart."
For the use case presented above, none of these modules (in conjunction with Drupal core) provides a solution.
Drupal 7 and 8 approach
In Drupal 7, the dual session situation is handled with two session IDs on a single record in the sessions table, one ID each for the insecure and secure sessions. Inherent to this design, the session data is automatically shared between the sessions. The cookie for the secure ID is only transmitted over a secure connection. To further address the problem of session hijacking, the session IDs are regenerated when the visitor switches to secure mode (referred to as a "step up").
However, even the Drupal 7 and 8 implementation has some flaws such as:
- after a user switches several times between HTTP and HTTPS, they end with "multiple" entries in the sessions table
- does not address security concerns with a step down from HTTPS to HTTP
These concerns are discussed in these issues:
A Drupal 6 solution
Out of all this, I created the Mixed Session module for Drupal 6 that:
- (AFAIK) securely implements mixed sessions
- regenerates session IDs on step up and step down
- preserves session data between session modes
- provides configurable rules for redirecting page requests to secure or insecure mode
- provides a configuration switch to stay secure once having entered secure mode
- includes a core patch to back-port the Drupal 7 dual session ID approach
- incorporates concepts mentioned in the issues referenced above
- transmits the secure ID only when in secure mode
- regenerates the insecure session ID when switching to of from secure mode
- implements four types of redirect rules to enter and exit secure mode
Redirect rules are available for:
- overall: never redirect, always redirect, or redirect based on rules below
- content: if the page includes a particular form (e.g. a login block)
- path: the request URL
- user state: anonymous or authenticated
The module also declares two API hooks through which a developer can incorporate more complex redirect rules.
With this module you can easily handle the client situation mentioned at the outset. You can also handle other configurations, such as:
- redirecting site administrators to view all site content in secure mode using HTTPS connections
- redirecting authenticated shoppers to secure mode when viewing their account pages.