Index: includes/session.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/session.inc,v
retrieving revision 1.91
diff -u -p -r1.91 session.inc
--- includes/session.inc	5 Nov 2010 19:05:02 -0000	1.91
+++ includes/session.inc	13 Nov 2010 10:34:54 -0000
@@ -88,10 +88,7 @@ function _drupal_session_read($sid) {
   // a HTTPS session or we are about to log in so we check the sessions table
   // for an anonymous session with the non-HTTPS-only cookie.
   if ($is_https) {
-    // Ensure that an empty secure session ID cannot be selected.
-    if ($sid) {
-      $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();
-    }
+    $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) {
       if (isset($_COOKIE[$insecure_session_name])) {
         $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(
@@ -239,7 +236,9 @@ function drupal_session_initialize() {
 
   session_set_save_handler('_drupal_session_open', '_drupal_session_close', '_drupal_session_read', '_drupal_session_write', '_drupal_session_destroy', '_drupal_session_garbage_collection');
 
-  if (isset($_COOKIE[session_name()]) || ($is_https && variable_get('https', FALSE) && isset($_COOKIE[substr(session_name(), 1)]))) {
+  // We use !empty() in the following check to ensure that blank session IDs
+  // are not valid.
+  if (!empty($_COOKIE[session_name()]) || ($is_https && variable_get('https', FALSE) && !empty($_COOKIE[substr(session_name(), 1)]))) {
     // If a session cookie exists, initialize the session. Otherwise the
     // session is only started on demand in drupal_session_commit(), making
     // anonymous users not use a session cookie unless something is stored in
Index: modules/simpletest/tests/session.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/session.test,v
retrieving revision 1.34
diff -u -p -r1.34 session.test
--- modules/simpletest/tests/session.test	5 Nov 2010 19:05:02 -0000	1.34
+++ modules/simpletest/tests/session.test	13 Nov 2010 10:34:54 -0000
@@ -223,6 +223,33 @@ class SessionTestCase extends DrupalWebT
   }
 
   /**
+   * Test that empty session IDs are not allowed.
+   */
+  function testEmptySessionID() {
+    $user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($user);
+    $this->drupalGet('session-test/is-logged-in');
+    $this->assertResponse(200, t('User is logged in.'));
+
+    // Reset the sid in {sessions} to a blank string. This may exist in the
+    // wild in some cases, although we normally prevent it from happening.
+    db_query("UPDATE {sessions} SET sid = '' WHERE uid = :uid", array(':uid' => $user->uid));
+    $sid = db_query('SELECT sid FROM {sessions} WHERE uid = :uid', array(':uid' => $user->uid))->fetchField();
+    $this->assertIdentical($sid, '', t('Session ID in {sessions} is blank.'));
+
+    // Send a blank sid in the session cookie, and the session should no longer
+    // be valid. Closing the curl handler will stop the previous session ID
+    // from persisting.
+    $this->curlClose();
+    $this->additionalCurlOptions[CURLOPT_COOKIE] = rawurlencode($this->session_name) . '=;';
+    $this->drupalGet('session-test/id-from-cookie');
+    $this->assertRaw("session_id:\n", t('Session ID is blank as sent from cookie header.'));
+    // Assert that we have an anonymous session now.
+    $this->drupalGet('session-test/is-logged-in');
+    $this->assertResponse(403, t('An empty session ID is not allowed.'));
+  }
+
+  /**
    * Reset the cookie file so that it refers to the specified user.
    *
    * @param $uid User id to set as the active session.
Index: modules/simpletest/tests/session_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/session_test.module,v
retrieving revision 1.15
diff -u -p -r1.15 session_test.module
--- modules/simpletest/tests/session_test.module	4 Dec 2009 16:49:47 -0000	1.15
+++ modules/simpletest/tests/session_test.module	13 Nov 2010 10:34:54 -0000
@@ -17,6 +17,12 @@ function session_test_menu() {
     'access arguments' => array('access content'),
     'type' => MENU_CALLBACK,
   );
+  $items['session-test/id-from-cookie'] = array(
+    'title' => 'Session ID from cookie',
+    'page callback' => '_session_test_id_from_cookie',
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
   $items['session-test/set/%'] = array(
     'title' => 'Set session value',
     'page callback' => '_session_test_set',
@@ -49,6 +55,12 @@ function session_test_menu() {
     'access arguments' => array('access content'),
     'type' => MENU_CALLBACK,
   );
+  $items['session-test/is-logged-in'] = array(
+    'title' => 'Check if user is logged in',
+    'page callback' => '_session_test_is_logged_in',
+    'access callback' => 'user_is_logged_in',
+    'type' => MENU_CALLBACK,
+  );
 
   return $items;
 }
@@ -104,6 +116,13 @@ function _session_test_id() {
 }
 
 /**
+ * Menu callback: print the current session ID as read from the cookie.
+ */
+function _session_test_id_from_cookie() {
+  return 'session_id:' . $_COOKIE[session_name()] . "\n";
+}
+
+/**
  * Menu callback, sets a message to me displayed on the following page.
  */
 function _session_test_set_message() {
@@ -165,3 +184,10 @@ function session_test_drupal_goto_alter(
     $path = $base_insecure_url . '/' . $path;
   }
 }
+
+/**
+ * Menu callback, only available if current user is logged in.
+ */
+function _session_test_is_logged_in() {
+  return t('User is logged in.');
+}
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.523
diff -u -p -r1.523 system.install
--- modules/system/system.install	13 Nov 2010 01:48:14 -0000	1.523
+++ modules/system/system.install	13 Nov 2010 10:34:55 -0000
@@ -2911,7 +2911,10 @@ function system_update_7065() {
     'length' => 128,
     'not null' => TRUE,
   );
-  db_change_field('sessions', 'sid', 'sid', $spec);
+  db_drop_primary_key('sessions');
+  db_change_field('sessions', 'sid', 'sid', $spec, array('primary key' => array('sid', 'ssid')));
+  // Delete any sessions with empty session ID.
+  db_delete('sessions')->condition('sid', '')->execute();
 }
 
 /**
