Switching between HTTP and HTTPS pages with secure session-cookie
Update: Note that every website switching between unsecure HTTP and encrypted HTTPS pages, is inevitable prone to SSL-strip. Please think about using HTTPS for the whole site, although this neither can prevent SSL-strip, at least this gives the user the possibility to call the site safely, if he cares. For sites that need to switch, this method is probably still the best option.
It's a common scenario, that a website has pages with sensitive data, which should be accessed only with the HTTPS protocoll, and other ones with noncritical data.
I found a solution which allows switching between secure and non secure pages, while keeping the session and would like to ask you for any hints about flaws in the concept. The whole article you can find here: Secure session cookie with SSL (of course i'm also happy to hear, that it is safe).
The problem
HTTPS makes sure, that nobody between client and server can eavesdrop our communication and prevents a man-in-the-middle attack. Unfortunately this doesn't apply to the session-cookie, it is sent to unencrypted requests too.
PHP offers the function session_set_cookie_params(...) with the parameter $secure. This is what we need, but it leaves us to the problem that we loose our session, when we switch to an unsecure page.
The authentication cookie
The idea of the authentication cookie is, that when the user enters his password (increases his access privileges), we create a second cookie additionally to the unsecure session-cookie, and make sure that only encrypted HTTPS pages have access to it.
https://www.example.com/login.php
<?php
session_start();
// regenerate session id to make session fixation more difficult
session_regenerate_id(true);
// generate random code for the authentication cookie and store it in the session
$authCode = md5(uniqid(mt_rand(), true));
$_SESSION['authentication'] = $authCode;
// create authentication cookie, and restrict it to HTTPS pages
setcookie('authentication', $authCode, 0, '/', '', true, true);
print('<h1>login</h1>');
...
?>
Now every page (HTTPS and HTTP) can read the unsecure session-cookie, but pages with sensitive information can check for the secure authentication cookie.
https://www.example.com/secret.php
<?php
session_start();
// check that the authentication cookie exists, and that
// it contains the same code which is stored in the session.
$pageIsSecure = (!empty($_COOKIE['authentication']))
&& ($_COOKIE['authentication'] === $_SESSION['authentication']);
if (!$pageIsSecure)
{
// do not display the page, redirect to the login page
}
...
?>
An attacker could manipulate the session cookie, but he never has access to the authentication cookie. Only the person who entered the password, can own the authentication cookie, it's always sent over encrypted HTTPS connections.
Thanks a lot for every answer!
A simpler alternative: It is becoming an increasingly accepted alternative to use TLS all the time, rather than switching back and forth between secure and unsecure connections. The bulk of additional processing time is spent setting up the secure tunnel, but this is only done once and cached (typically). The symmetric encryption of subsequent traffic is very, very fast on modern processors. It's somewhat out-of-date thinking to believe that this would cause a server overhead or scalability issue.
In a recent blog post, a Google engineer reported that when they switched to HTTPS-only for GMail, they found their server overheard increased by only 4%. (Can't find the citation.)