The default installation of Drupal transmits username/passsword in cleartext. I would like to secure my Drupal installation with the following goals in mind:

  • Anonymous users should be able to access the entire web-site over http (non-secure)
  • Authenticated users should be provided access only over secure https

I have read several posts on this topic but non of those provide a complete solution all on one page. So, I have come up with 4 step approach to acheive this. I am relatively new to drupal so please correct me if I have missed anything in this setup.

Step 1

Set the following parameter in your apache configuration (.htaccess file or httpd.conf) file:

# Allow cookie session over secure channel only
php_value session.cookie_secure 1 

This will instruct php to handle session cookies over secure (https) channel only. When users connect via the regular http URL, they will always be "anonymous," even if they have a valid session on the https site [1].

Step 2

Specify https protocol in your drupal/sites/default/settings.php file:

# All absolute URLs emitted by drupal will contain https
$base_url = 'https://example.com';  // NO trailing slash!

Your web-site will be accessible over both http and https as long as the apache configuration files are setup to map both the protocols to the same drupal installation. Most of the pages generated by drupal do not contain absolute URLs. The URLs generated for the various nodes within the web-site are of the form /node/123. As a result, a regular user visiting http://example.com will be able to navigate through the entire web-site even though the base_url points to https. Or users can directly access https://example.com for secure access.

The reasoning behind using https for base url will become apparent in step 4 below.

Step 3

Redirect all http requests for /user or /admin pages to go over https. This can be implemented by adding the following lines to your apache configuration file [2]:

# For secure connections, all incoming requests for /user
# and /admin should be redirected to secure https site.     
# Note: Place this before the Rewrite rule for clean URLs.
#           This will ensure that clean URLs do not get mapped
#           into regular URLs before it gets re-directed.
#
# Clean urls: if the requested resource begins with
#             "/user" or "/admin"
RewriteCond %{REQUEST_URI} ^/(user|admin) [OR]
#
# _OR_
# Standard urls: if the requested resource begins with
#                "q=user" or "q=admin"
RewriteCond %{QUERY_STRING} ^q=(user|admin)
#
# we want https, so rewrite and redirect the entire url ("^(.*)$"),
# prepending "https://" and appending the above-matched
# resource or string to the base server name
RewriteRule ^(.*)$ https://%{SERVER_NAME}/$1 [L,R=301]
#
# Rewrite rules for clean URL below ...

This will work only if the user is directly trying to access http://example.com/user page. It will not work for username/password submitted via login box [3]. In combination with the secure cookie setting in step 1, drupal will prevent users from getting a valid session id if they try to logon over http via the login box, but the password will still be sent in cleartext.

Step 4

Patch the drupal/modules/user.module file to always return absolute path containing https protocol for the login box:

~/src/drupal/modules$ cvs diff -u user.module
Index: user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user.module,v
retrieving revision 1.594
diff -u -r1.594 user.module
--- user.module 10 Mar 2006 15:11:07 -0000      1.594
+++ user.module 11 Mar 2006 20:43:04 -0000
@@ -521,7 +521,7 @@
       case 0:
         // For usability's sake, avoid showing two login forms on one page.
         if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
-          $form['#action'] = url($_GET['q'], drupal_get_destination());
+          $form['#action'] = url($_GET['q'], drupal_get_destination(), NULL, TRUE);
           $form['#id'] = 'user-login-form';
           $form['name'] = array('#type' => 'textfield',
             '#title' => t('Username'),
@@ -539,9 +539,9 @@
           );

           if (variable_get('user_register', 1)) {
-            $items[] = l(t('Create new account'), 'user/register', array('title' => t('Create a new user account.')));
+            $items[] = l(t('Create new account'), 'user/register', array('title' => t('Create a new user account.')), NULL, NULL, TRUE);
           }
-          $items[] = l(t('Request new password'), 'user/password', array('title' => t('Request new password via e-mail.')));
+          $items[] = l(t('Request new password'), 'user/password', array('title' => t('Request new password via e-mail.')), NULL, NULL, TRUE);
           $form['links'] = array('#value' => theme('item_list', $items));

           $output .= drupal_get_form('user_login_block', $form, 'user_login');
~/src/drupal/modules$

This patch was created against cvs head. It requests the url and l functions to return absolute urls for the following cases:

  • user login page
  • new user account page
  • new password page

Note that we have to explicitly set the base url to use https protocol in step 2. This ensures that the login box will use secure channel by appropriately configuring the settings.php file.

Conclusion

I have not fully investigated as to what other URLs or modules should be using secure https. So it is quite possible that there will be places in regular http where it will still end up sending sensitive information in cleartext.

I have tried to come up with an approach that requires minimum changes to the core drupal files. If there is a better way to do this, please let me know. I am also hoping that my patch to user.module file, if found to be reasonable, can be incorporated into drupal4.7 (or is it too late now?).

Comments

mfb’s picture

In your apache config, you must specify different session.names for the http and https virtual hosts. Otherwise, users logged in on the https site will have their session overwritten if they visit the http site (e.g. by following an external link), because the session cookie is never sent to the http site.

This configuration causes a new session to be generated each time a user visits an http URL (causing some overhead). To avoid this, you could set session.cookie_secure only on the https virtual host.

dkg’s picture

Even if your proposal covers everything that needs to be wrapped in TLS, I don't think this mixed http/https strategy is a good idea, mainly because of the confusion it causes for logged-in users. To secure the site and avoid confusion, you're better off going all-https.

Consider the following scenario:

  1. user logs in (this happens over HTTPS)
  2. browser receives secure, https-only cookie from your drupal site
  3. user follows external link (e.g. google only sees http:// links as an anonymous spider), bookmark, or e-mailed link that only uses http://
  4. the site produces a reasonable page, but not the page the authenticated user should see (because hir cookie wasn't transmitted). this page may even show a "login" link, or demand that the user log in to post a comment
  5. user clicks the login link, and finds that sie's already logged in(!)

Unless your users are really savvy about the interactions between cookies, site authentication, http, and https, you're going to have some seriously confused users. Those users will tend to perceive your site as broken, because sometimes their login "just goes away" and then magically re-appears.

Show enough "access denied" or "login to post a comment" messages to a user who has already logged in, and that user won't be inclined to return.

christefano’s picture

#170310 (session.cookie_secure: SSL cookie gets over-written by non-SSL cookie) has landed in core.

ganymede’s picture

hello, HemangLavana

I have some problems about this topic, and I'm trying my best asking help to get the result, because I love drupal, and I need exactly the type of solution you suggest

please notice that I'm not a programmer, I have done two days of attempts, so forgive my lack of competence, anyway I hope that this topic will get to be easy for every user interested in drupal

I installed drupal 6.4 locally, in XAMPP, directly in htdocs (so my installation could be reached at http://localhost/ )

here it is the situation

1 - "step 3" gives a redirect loop, I don't know how to fix it

2 - I tried, but I have no idea how to apply "step 4" (really interesting for usability), could you help to apply the patch manually in 6.4 version (and in future update)?
is this patch from you ( http://drupal.org/node/53618 ) an alternative?
and could this comment http://drupal.org/node/1577#comment-962192 be another solution for this?

3 - the solution at http://drupal.org/node/170310 (different session names for HTTP and HTTPS) doesn't work for me, please see my forum post at http://drupal.org/node/314511 for details
is that patch related with this technique for secure cookies?

I hope in your help for this important topic,

thanks a lot,

ganymede