This module could be used to resolve the age old user login block issue, by allowing extra pages to be defined for anonymous users. Then users could specify all pages that the login block resides on to be secured. Once logged in, the extra pages are ignored.

The use case for this was to secure a site using an embedded login block on the front page.

My patches keep failing, so here is the code that I implemented from version 6.x-1.7-beta1.

<?php

function securepages_uninstall() {
  variable_del('securepages_enable');
  variable_del('securepages_switch');
  variable_del('securepages_secure');
  variable_del('securepages_pages');
  variable_del('securepages_ignore');
  variable_del('securepages_anonymous_pages');
}

?>

<?php

/**
 * Implementation of hook_settings().
 */
function securepages_settings() {
// ...
  $form['securepages_pages'] = array(
    '#type' => 'textarea',
    '#title' => t('Pages'),
    '#default_value' => variable_get('securepages_pages', "node/add*\nnode/*/edit\nuser/*\nadmin*"),
    '#cols' => 40,
    '#rows' => 5,
    '#description' => t("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['securepages_anonymous_pages'] = array(
    '#type' => 'textarea',
    '#title' => t('Additional anonymous pages'),
    '#default_value' => variable_get('securepages_anonymous_pages', ''),
    '#cols' => 40,
    '#rows' => 5,
    '#description' => t("Additional pages to secure for anonymous users. Enter one page per line as."),
  );
// ...
}

function securepages_match($path) {
  global $user;
  /**
   * Check to see if the current menu item has a preference and ignore the
   * secure pages settings
   */
  if (function_exists('menu_get_item')) {
    $item = menu_get_item($path);
    if (isset($item['secure'])) {
      return $item['secure'];
    }
  }

  /**
   * Check to see if the page matches the current settings
   */
  $secure = variable_get('securepages_secure', 1);
  $pages = variable_get('securepages_pages', "node/add*\nnode/*/edit\nuser/*\nadmin*");
  if (!$user->uid) {
    $anonymous_pages = variable_get('securepages_anonymous_pages', '');
    $pages .= $anonymous_pages ? "\n". $anonymous_pages : '';
  }
// ...
}

?>

Comments

alan d.’s picture

The last thing that I had to lock down a URL that was show to both anonymous users and members. A custom 404/403 page (front page). Both of these had the login block to secure.

To do this I created a custom menu callback and assigned the error handler pages to this.

<?php
  $items['jm_custom_error_page'] = array(
    'title'  => t('Error page'),
    'page callback' => 'jm_custom_error',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
    'file' => 'custom_error_page.inc',
    'file path' => drupal_get_path('module', 'jm') .'/pages',
  );
?>

And then made this callback function:

<?php
/**
 * Simple redirect handler to send the user to the dashboard when there is
 * a 404 / 403. This enables the Secure pages module to trigger HTTPS if
 * required.
 */
function jm_custom_error() {
  global $user;
  
  $headers = drupal_get_headers();
  $http_response_code = 302;
  
  if (stripos($headers, '404') !== FALSE) {
    $message = $user->uid 
      ? t('PAGE NOT FOUND: The requested page could not be found.')
      : t('PAGE NOT FOUND: The requested page could not be found. You may need to login to access it.');
    drupal_set_message($message, 'error');
  }
  elseif (stripos($headers, '403') !== FALSE) {
    drupal_set_message(t('ACCESS DENIED: You are not authorised to access this page.'), 'error');
  }
  $dashboard = url(drupal_get_normal_path(variable_get('site_frontpage', 'node')), array('absolute' => TRUE));
  
  if (!$user->uid) {
    if (isset($_REQUEST['destination'])) {
      $dashboard .= '?destination='. urlencode($_REQUEST['destination']);
    }    
  }

  // Allow modules to react to the end of the page request before redirecting.
  // We do not want this while running update.php.
  if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
    module_invoke_all('exit', $dashboard);
  }

  // Even though session_write_close() is registered as a shutdown function, we
  // need all session data written to the database before redirecting.
  session_write_close();

  header('Location: '. str_replace(array("\n", "\r"), '', $dashboard), TRUE, $http_response_code);

  // The "Location" header sends a redirect status code to the HTTP daemon. In
  // some cases this can be wrong, so we make sure none of the code below the
  // drupal_goto() call gets executed upon redirection.
  exit();
    
}
?>

Now I can flick the front page from HTTP to HTTPS depending on the user status and also assign the 404/403 pages here as well. The login block to the front page will always be secure. :)

If anyone has a better approach to this, I would be interested in it.

alan d.’s picture

Issue tags: +Security improvements

bump. This is a cool feature that fixes a security hole in Drupal - transmission of user passwords via HTTP

grendzy’s picture

Status: Active » Closed (won't fix)

IMHO, the best solution is to use http://drupal.org/project/securepages_prevent_hijack , which ensures passwords are sent over SSL. Securing the login form by itself has almost no value, because you're still vulnerable to session hijacking.