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 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; +} 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 @@ 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('

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 'blog' for the blog page and 'blog/*' for every personal blog. '<front>' 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.