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('
The Secure Pages module has detected an invalid '. + 'session access attempt. Please log in again.
', + 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 'blog' for the blog page and 'blog/*' for every personal blog. '<front>' 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 hijack-prevention feature of Securepages, '. + 'you should disable session.cookie_secure 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 enabling hijack prevention, (recommended) '. + 'or by setting session.cookie_secure 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 '. + 'Secure Pages settings; make sure user* is secure.', + array('@securepages_admin' => url('admin/settings/securepages'))); + } + + return $requirements; +}