Index: securepages.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/securepages/securepages.module,v
retrieving revision 1.15.2.10
diff -u -p -r1.15.2.10 securepages.module
--- securepages.module	17 Nov 2008 02:09:58 -0000	1.15.2.10
+++ securepages.module	12 Dec 2008 00:07:09 -0000
@@ -6,6 +6,8 @@
  * https pages
  */
 
+define('SECUREPAGES_SESSID', 'SSL_'. session_name());
+
 /**
  * Implementation of hook_boot().
  */
@@ -61,6 +63,32 @@ function securepages_init() {
   else {
     unset($_SESSION['securepages_redirect']);
   }
+  $path = isset($_GET['q']) ? $_GET['q'] : '';
+  $page_match = securepages_match($path);
+
+  // 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' => $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 <a href="!url">log in again</a>.</p>',
+        array('!url' => url('logout', array('query' => array('destination' => 'user/login')))));
+      print theme('page', $return);
+      module_invoke_all('exit', $url);
+      exit;
+    }
+  }
 }
 
 /**
@@ -103,6 +131,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'),
@@ -137,10 +172,22 @@ 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."),
   );
+  $form['#submit'][] = '_securepages_prevent_hijack_submit';
   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, &$form_state) {
+  if ($form_state['values']['securepages_prevent_hijack']) {
+    _securepages_prevent_hijack_cookie();
+  }
+}
+
+/**
  * Implementation of hook_form_alter().
  */
 function securepages_form_alter(&$form, &$form_state, $form_id) {
@@ -214,6 +261,37 @@ function securepages_redirect() {
   }
 }
 
+ /**
+ * 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.'));
+      }
+
+      _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()
  *
Index: securepages.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/securepages/securepages.install,v
retrieving revision 1.2.2.5
diff -u -p -r1.2.2.5 securepages.install
--- securepages.install	15 Nov 2008 00:21:05 -0000	1.2.2.5
+++ securepages.install	12 Dec 2008 00:07:09 -0000
@@ -31,3 +31,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;
+}
