Index: securepages.install
===================================================================
RCS file: /cvs/drupal/contributions/modules/securepages/securepages.install,v
retrieving revision 1.1.2.3
diff -u -p -r1.1.2.3 securepages.install
--- securepages.install	15 Nov 2008 00:44:26 -0000	1.1.2.3
+++ securepages.install	7 Jan 2009 00:07:24 -0000
@@ -27,3 +27,59 @@ function securepages_update_1() {
 
   return array();
 }
+
+/**
+ * Implementation of hook_requirements().
+ *
+ */
+function securepages_requirements($phase) {
+  $requirements = array();
+
+  if (! variable_get('securepages_enable', 0)) {
+    return $requirements;
+  }
+
+  if ($phase == 'runtime') {
+    $requirements['securepages_prevent_hijack']['title'] = t('Secure Pages hijack prevention');
+
+    if (variable_get('securepages_prevent_hijack', FALSE) && ini_get('session.cookie_secure')) {
+      $requirements['securepages_prevent_hijack']['severity'] = REQUIREMENT_WARNING;
+      $requirements['securepages_prevent_hijack']['value'] = t('Secure');
+      $requirements['securepages_prevent_hijack']['description'] = t('Securepages settings conflict with your PHP configuration. '.
+        'When using the <a href="@securepages_admin">hijack-prevention feature of Securepages</a>, '.
+        'you should disable <a href="@php_manual">session.cookie_secure</a> in your PHP settings. '.
+        'This will allow users to stay logged in even as they browse non-secure pages.',
+        array('@securepages_admin' => url('admin/settings/securepages'), '@php_manual' => 'http://php.net/session.configuration'));
+    }
+    else if (! variable_get('securepages_prevent_hijack', FALSE) && ! ini_get('session.cookie_secure')) {
+      $requirements['securepages_prevent_hijack']['severity'] = REQUIREMENT_ERROR;
+      $requirements['securepages_prevent_hijack']['value'] = t('Insecure');
+      $requirements['securepages_prevent_hijack']['description'] = t('Your site is vulnerable to session hijacking. '.
+        'You can fix this by <a href="@securepages_admin">enabling hijack prevention</a>, (recommended) '.
+        'or by setting <a href="@php_manual">session.cookie_secure</a> in your PHP settings.',
+        array('@securepages_admin' => url('admin/settings/securepages'), '@php_manual' => 'http://php.net/session.configuration'));
+    }
+    else if (variable_get('securepages_prevent_hijack', FALSE)) {
+      $requirements['securepages_prevent_hijack']['severity'] = REQUIREMENT_OK;
+      $requirements['securepages_prevent_hijack']['value'] = t('Secure');
+      $requirements['securepages_prevent_hijack']['description'] = t('Your site is configured to prevent hijacked sessions from accessing SSL pages.');
+    }
+    else if (ini_get('session.cookie_secure')) {
+      $requirements['securepages_prevent_hijack']['severity'] = REQUIREMENT_OK;
+      $requirements['securepages_prevent_hijack']['value'] = t('Secure');
+      $requirements['securepages_prevent_hijack']['description'] = t('Your site is configured to prevent session hijacking.');
+    }
+  }
+
+  if (variable_get('securepages_prevent_hijack', FALSE) && ! securepages_match('user')) {
+    $requirements['securepages_user']['title'] = t('Secure Pages login form');
+    $requirements['securepages_user']['severity'] = REQUIREMENT_ERROR;
+    $requirements['securepages_user']['value'] = t('Insecure');
+    $requirements['securepages_user']['description'] = t('Secure Pages is set to prevent hijacked sessions '.
+      'from accessing SSL pages but the user login page is not set to be secured.  Fix your '.
+      '<a href="@securepages_admin">Secure Pages settings</a>; make sure <em>user*</em> is secure.',
+      array('@securepages_admin' => url('admin/settings/securepages')));
+  }
+
+  return $requirements;
+}
Index: securepages.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/securepages/securepages.module,v
retrieving revision 1.12.2.11
diff -u -p -r1.12.2.11 securepages.module
--- securepages.module	7 Aug 2008 01:05:11 -0000	1.12.2.11
+++ securepages.module	7 Jan 2009 00:07:24 -0000
@@ -1,6 +1,13 @@
 <?php
 // $Id: securepages.module,v 1.12.2.11 2008/08/07 01:05:11 gordon Exp $
 
+// Include $base_path in SECUREPAGES_SESSID so a user can be logged in
+// to more than one Drupal site per domain.
+global $base_path;
+define('SECUREPAGES_SESSID', 
+       'SECUREPAGES_SESSID_'.preg_replace('/[^a-zA-Z0-9_]/', '_', $base_path));
+
+
 /**
  * Implementation of hook_init()
  */
@@ -37,6 +44,33 @@ function securepages_init() {
   if (securepages_is_secure()) {
     $base_url = securepages_baseurl();
   }
+
+  // If this is and is supposed to be a secure page, and a user has
+  // logged in, verify that the secure cookie (set in securepages_user
+  // hook below) matching the secure token in $_SESSION is present.
+  global $user;
+  if ($user->uid > 0 && $page_match && $_SERVER['HTTPS'] &&
+      variable_get('securepages_prevent_hijack', FALSE)) {
+    if (is_null($_COOKIE[SECUREPAGES_SESSID]) ||
+	$_COOKIE[SECUREPAGES_SESSID] !== $_SESSION[SECUREPAGES_SESSID]) {
+      watchdog('security', 
+	       t('Session hijack attempt detected for user %user!',
+		 array('%user' => theme('placeholder', $user->name))));
+
+      menu_set_active_item('');
+      drupal_set_header('HTTP/1.1 403 Forbidden');
+      drupal_set_title(t('Access denied by Secure Pages module'));
+      $return = t('<p>The Secure Pages module has detected an invalid '.
+		  'session access attempt.  Please '.
+		  l('log in again', 'logout', array(), 
+		    'destination=user/login') .
+		  '.');
+      print theme('page', $return);
+      module_invoke_all('exit', $url);
+      exit;
+    }
+  }
+
 }
 
 /**
@@ -79,6 +113,13 @@ function securepages_settings() {
     '#default_value' => variable_get('securepages_switch', FALSE),
   );
 
+  $form['securepages_prevent_hijack'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Prevent hijacked sessions from accessing SSL pages'),
+    '#return_value' => TRUE,
+    '#default_value' => variable_get('securepages_prevent_hijack', FALSE),
+  );
+
   $form['securepages_basepath'] = array(
     '#type' => 'textfield',
     '#title' => t('Non-secure Base URL'),
@@ -113,10 +154,28 @@ function securepages_settings() {
     '#rows' => 5,
     '#description' => t("The pages listed here will be ignored and be either returned in http or https. Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are '<em>blog</em>' for the blog page and '<em>blog/*</em>' for every personal blog. '<em>&lt;front&gt;</em>' is the front page."),
   );
+
+  // In order to give it our own submit, we have to give it the default submit
+  // too because the presence of a #submit will prevent the default #submit
+  // from being used. Also we want ours first.
+  $form['#submit']['_securepages_prevent_hijack_submit'] = array();
+  $form['#submit']['system_settings_form_submit'] = array();
+
   return system_settings_form($form);
 }
 
 /**
+ * Helper for securepages_settings.
+ * This prevents the user from being logged out the first time
+ * hijack prevention is enabled.
+ */
+function _securepages_prevent_hijack_submit($form_id, $form_values) {
+  if ($form_values['securepages_prevent_hijack']) {
+    _securepages_prevent_hijack_cookie();
+  }
+}
+
+/**
  * Implementation of hook_form_alter()
  */
 function securepages_form_alter($form_id, &$form) {
@@ -167,6 +226,40 @@ function securepages_link_alter(&$node, 
 }
 
 /**
+ * Implementation of hook_user()
+ */
+function securepages_user($op, &$edit, &$user, $category = NULL) {
+  switch ($op) {
+  case 'login':
+    if (variable_get('securepages_prevent_hijack', FALSE)) {
+      if (! $_SERVER['HTTPS']) {
+	    // Admin asked us to prevent hijacks but we have a non-secure login.
+	    watchdog('security', t('Secure Pages detected non-SSL login '.
+	      'with hijack-prevention enabled.'));
+      }
+
+      // The user has just logged in.  Set a secure cookie (that will
+      // only be returned to SSL-protected pages) containing a
+      // non-guessable token, and store that token in the $_SESSION.
+      _securepages_prevent_hijack_cookie();
+    }
+    break;
+  }
+}
+
+/**
+ * Set a secure cookie (that will only be returned to SSL-protected pages)
+ * containing a non-guessable token, and store that token in the $_SESSION.
+ */
+function _securepages_prevent_hijack_cookie() {
+  $tok = md5(mt_rand() . $edit['pass'] . mt_rand());
+  $_SESSION[SECUREPAGES_SESSID] = "$tok";
+  $cookie_params = session_get_cookie_params();
+  setcookie(SECUREPAGES_SESSID, $tok, time() + $cookie_params['lifetime'],
+    $cookie_params['path'], $cookie_params['domain'], 1);
+}
+
+/**
  * securepage_goto()
  *
  * Redirects the current page to the secure or insecure version.
