=== modified file 'includes/bootstrap.inc' --- includes/bootstrap.inc 2009-07-28 12:13:46 +0000 +++ includes/bootstrap.inc 2009-08-01 07:00:33 +0000 @@ -518,7 +518,7 @@ function drupal_settings_initialize() { global $base_url, $base_path, $base_root; // Export the following settings.php variables to the global namespace - global $databases, $db_prefix, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url; + global $databases, $db_prefix, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $is_https; $conf = array(); if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) { @@ -528,6 +528,7 @@ function drupal_settings_initialize() { if (isset($base_url)) { // Parse fixed base URL from settings.php. $parts = parse_url($base_url); + $http_protocol = $parts['scheme']; if (!isset($parts['path'])) { $parts['path'] = ''; } @@ -537,9 +538,10 @@ function drupal_settings_initialize() { } else { // Create base URL - $base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; + $http_protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; + $base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST']; - $base_url = $base_root .= '://' . $_SERVER['HTTP_HOST']; + $base_url = $base_root; // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not // be modified by a visitor. @@ -552,6 +554,7 @@ function drupal_settings_initialize() { $base_path = '/'; } } + $is_https = $http_protocol == 'https'; if ($cookie_domain) { // If the user specifies the cookie domain, also use it for session name. @@ -566,15 +569,6 @@ function drupal_settings_initialize() { $cookie_domain = check_plain($_SERVER['HTTP_HOST']); } } - // To prevent session cookies from being hijacked, a user can configure the - // SSL version of their website to only transfer session cookies via SSL by - // using PHP's session.cookie_secure setting. The browser will then use two - // separate session cookies for the HTTPS and HTTP versions of the site. So we - // must use different session identifiers for HTTPS and HTTP to prevent a - // cookie collision. - if (ini_get('session.cookie_secure')) { - $session_name .= 'SSL'; - } // Strip leading periods, www., and port numbers from cookie domain. $cookie_domain = ltrim($cookie_domain, '.'); if (strpos($cookie_domain, 'www.') === 0) { @@ -587,7 +581,17 @@ function drupal_settings_initialize() { if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) { ini_set('session.cookie_domain', $cookie_domain); } - session_name('SESS' . md5($session_name)); + // To prevent session cookies from being hijacked, a user can configure the + // SSL version of their website to only transfer session cookies via SSL by + // using PHP's session.cookie_secure setting. The browser will then use two + // separate session cookies for the HTTPS and HTTP versions of the site. So we + // must use different session identifiers for HTTPS and HTTP to prevent a + // cookie collision. + if ($is_https) { + ini_set('session.cookie_secure', TRUE); + } + $prefix = ini_get('session.cookie_secure') ? 'SSESS' : 'SESS'; + session_name($prefix . md5($session_name)); } /** === modified file 'includes/common.inc' --- includes/common.inc 2009-07-31 07:27:59 +0000 +++ includes/common.inc 2009-08-02 01:26:45 +0000 @@ -350,7 +350,7 @@ function drupal_goto($path = '', $query // database before redirecting. drupal_session_commit(); - header('Location: ' . $url, TRUE, $http_response_code); + header('Location: ' . str_replace('https://', 'http://', $url), TRUE, $http_response_code); // The "Location" header sends a redirect status code to the HTTP daemon. In // some cases this can be wrong, so we make sure none of the code below the === modified file 'includes/session.inc' --- includes/session.inc 2009-07-01 12:47:30 +0000 +++ includes/session.inc 2009-08-02 02:39:36 +0000 @@ -66,7 +66,7 @@ function _drupal_session_close() { * was found or the user is anonymous. */ function _drupal_session_read($sid) { - global $user; + global $user, $is_https; // Write and Close handlers are called after destructing objects // since PHP 5.0.5. @@ -82,8 +82,23 @@ function _drupal_session_read($sid) { } // Otherwise, if the session is still active, we have a record of the - // client's session in the database. - $user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid", array(':sid' => $sid))->fetchObject(); + // client's session in the database. If it's HTTPS then we are either have + // a HTTPS session or we are about to log in so we check the sessions table + // for an anonymous session wth the non-HTTPS-only cookie. + if ($is_https) { + $user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.ssid = :ssid", array(':ssid' => $sid))->fetchObject(); + if (!$user) { + $unsecure_sid = substr(session_name(), 1); + if (isset($_COOKIE[$unsecure_sid])) { + $user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid AND s.uid = 0", array( + ':sid' => $_COOKIE[$unsecure_sid])) + ->fetchObject(); + } + } + } + else { + $user = db_query("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = :sid", array(':sid' => $sid))->fetchObject(); + } // We found the client's session record and they are an authenticated user. if ($user && $user->uid > 0) { @@ -122,22 +137,28 @@ function _drupal_session_read($sid) { * This function will always return TRUE. */ function _drupal_session_write($sid, $value) { - global $user; + global $user, $is_https; if (!drupal_save_session()) { // We don't have anything to do if we are not allowed to save the session. return; } + $fields = array( + 'uid' => $user->uid, + 'cache' => isset($user->cache) ? $user->cache : 0, + 'hostname' => ip_address(), + 'session' => $value, + 'timestamp' => REQUEST_TIME, + ); + $unsecure_sid = substr(session_name(), 1); + watchdog('test', var_export($_COOKIE, TRUE). $unsecure_sid); + if ($is_https && isset($_COOKIE[$unsecure_sid])) { + $fields['sid'] = $_COOKIE[$unsecure_sid]; + } db_merge('sessions') - ->key(array('sid' => $sid)) - ->fields(array( - 'uid' => $user->uid, - 'cache' => isset($user->cache) ? $user->cache : 0, - 'hostname' => ip_address(), - 'session' => $value, - 'timestamp' => REQUEST_TIME, - )) + ->key(array($is_https ? 'ssid' : 'sid' => $sid)) + ->fields($fields) ->execute(); // Last access time is updated no more frequently than once every 180 seconds. @@ -246,7 +267,7 @@ function drupal_session_started($set = N * Called when an anonymous user becomes authenticated or vice-versa. */ function drupal_session_regenerate() { - global $user; + global $user, $is_https; if (drupal_session_started()) { $old_session_id = session_id(); @@ -259,12 +280,18 @@ function drupal_session_regenerate() { $account = $user; drupal_session_start(); $user = $account; + if ($is_https) { + $insecure_sid = substr(session_name(), 1); + $params = session_get_cookie_params(); + setcookie($insecure_sid, session_id(), REQUEST_TIME + $params['lifetime'], $params['path'], $params['domain'], FALSE, $params['httponly']); + $_COOKIE[$insecure_sid] = session_id(); + } } if (isset($old_session_id)) { db_update('sessions') ->fields(array( - 'sid' => session_id() + $is_https ? 'ssid' : 'sid' => session_id() )) ->condition('sid', $old_session_id) ->execute(); === modified file 'modules/system/system.install' --- modules/system/system.install 2009-07-30 19:27:11 +0000 +++ modules/system/system.install 2009-08-02 00:57:04 +0000 @@ -1319,6 +1319,13 @@ function system_schema() { 'not null' => TRUE, 'default' => '', ), + 'ssid' => array( + 'description' => "Unique key: Secure session ID. The value is generated by PHP's Session API.", + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ), 'hostname' => array( 'description' => 'The IP address that last used this session ID (sid).', 'type' => 'varchar', @@ -1350,6 +1357,9 @@ function system_schema() { 'timestamp' => array('timestamp'), 'uid' => array('uid'), ), + 'unique keys' => array( + 'ssid' => array('ssid'), + ), 'foreign keys' => array( 'uid' => array('users' => 'uid'), ), === modified file 'modules/user/user.module' --- modules/user/user.module 2009-07-31 19:01:01 +0000 +++ modules/user/user.module 2009-08-02 01:23:21 +0000 @@ -985,7 +985,7 @@ function user_user_categories() { function user_login_block() { $form = array( - '#action' => url($_GET['q'], array('query' => drupal_get_destination())), + '#action' => str_replace('http://', 'https://', url($_GET['q'], array('absolute' => TRUE, 'query' => drupal_get_destination()))), '#id' => 'user-login-form', '#validate' => user_login_default_validators(), '#submit' => array('user_login_submit'),