Securing Drupal User Accounts

30 November -0001

By Justin Klein Keane

Drupal is a robust content management system (CMS). If you haven't heard of Drupal you can check it out at Drupa.org. Drupal is open source and free for download. Drupal is implemented on a typical LAMP stack. Drupal is extremely powerful and extensible using hundreds of third party modules. Unfortunately the flexibility and customization options of Drupal mean that it is often difficult to set up a secure Drupal instance.

Securing a default Drupal installation takes some work and forethought. Drupal has a number of extremely helpful features that enable users to do some powerful things, but many of these features can be used for malicious purposes in the wrong hands. Some of the Drupal features that were intended to make users lives easier are extremely functional, but others only serve a small minority. The features that open vulnerability gaps that are not employed by most Drupal users should be disabled, or removed entirely. For the purposes of this article I'll limit my discussion to Drupal 6, although many of the features discussed also exist in Drupal 5.

Account Creation

Drupal handles user security in a fairly straightforward manner. User creation is highly configurable, however, and care must be taken when establishing rules for new user accounts. User generated content is a powerful tool to legitimate websites, but it can also be abused. Drupal is a popular target for spammers and link jackers who try to leverage existing websites to post links to their own sites. It is common to allow account holders on Drupal sites to post content, either in the form of comments, or articles, blogs or other content. Malicious site users may try to leverage your site for so-called "black hat SEO." The idea is that search engines determine content rank and relevance based on links to that content. The bad guys set up websites and try to inject links to their sites into other, often unsuspecting legitimate sites. This can clutter up your site with useless text and links to outside sites. Not only does this degrade the quality of your site, but it also results in your users associating the content (and links) with your site. Unless you want to be associated with promoting fake perscription drugs take care so that bad guys can't post unregulated content to your site.

Limiting account creation is critical to ensuring that only legitimate users can create accounts. Ensure that users requesting new accounts either respond to e-mail confirmations or require administrator approval prior to the creation of accounts. This helps to limit automated account creation attempts (by bots). This configuration can be accessed via the main Drupal navigation menu following the links for Administer -> User settings. The 'User settings' page includes configuration options for how account creation authorization occurs as well as templates for e-mail messages sent to new user or existing users.

Sending passwords over plain-text e-mail is never a good idea. Email is a popular target for hackers, and if users store e-mails with passwords in their inbox they could be compromised. Additionally, anyone who can eavesdrop on a user's connection to their e-mail server could read passwords in e-mail. On the 'User settings' page you'll notice that some of the content of the e-mail templates are tokenized, including place holders such as "!username" and "!password." These tokens are replaced with user specific values before e-mails are sent. It is important to remove any occurrence of "!password" token to prevent user passwords being sent via e-mail. Without the password users must utilize a time sensitive link in order to activate their account or change their password. This is a well known scheme and is much safer than sending account credentials over unencrypted e-mail where they propagate amongst mail servers, handhelds, and various end user workstations, ripe for theft.

Account Security

In addition to account creation it is important to enforce security in the accounts themselves. Drupal does provide a password strength indicator for users when creating new accounts (letting users know if they've chosen a strong password), but no strength requirements are enforced. You can install and utilize the Password Strength module (http://drupal.org/project/password_strength) in order to require that users select strong passwords. Strong passwords are the best way to protect against password guessing attacks.

Another great module for protecting user accounts is the Login Security module (http://drupal.org/project/login_security). This module detects brute force, or automated password guessing, attacks and can prevent them by notifying administrators and locking accounts for a time. The module allows administrators to set limits on the number of times a user can attempt to log in before blocking that user (or machine). If you take care and set up high limits (like 12 failed attempts in 5 minutes) you can avoid locking out legitimate users but easily defeat programs that will try to maximize their password guessing attempts. The Login Security module can also implement nifty features like a response delay, so that after a failed login attempt the resulting page takes longer and longer to load. This is very effective for defeating programs, and may annoy users if they forget their passwords or fail to log in several times, but it avoids the problem of actually locking a legitimate user out of the system.

Encrypted Protocols

Another problem with Drupal user security is the fact that users are not forced to utilize SSL in order to log in. This means that when users log into the Drupal site or administer the site their communications pass over clear text and are liable to snooping. Although there is a module that can enforce SSL it is still in development status at the time of this writing. One can alter the .htaccess file of their Drupal site to easily require SSL, however. The .htaccess file is a text file on the server where Drupal is installed. It is critical to ensure that sites have a valid SSL certificate before enforcing this type of restriction to prevent errors. Adding the following snippet to the Drupal .htaccess file should be sufficient to enforce encryption during login and administration:

# Force the user to use https.
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule /(user|admin) https://%{HTTP_HOST}%{REQUEST_URI} [R]

# Enfore SSL for user login and administration.
<LocationMatch "/(user|admin)">
    SSLRequireSSL
</LocationMatch>

Thanks to http://kkaefer.com/blog/securing-drupal-s-user-login for this snippet of code.

By requiring SSL you make sure that the credentials submitted during login and the cookie that is used for authentication after login, are encrypted during transmission and can't be used by an attacker to steal account credentials. The only problem with this setup is that if a user logs in, then visits a regular node page they can view the page of unencrypted HTTP. This allows anyone who is watching the traffic to view the cookie used for authentication after login. Armed with this cookie an attacker can "hijack" a session and bypass login for authentication. To protect against this threat you can use the PHP setcookie() function to require that cookies only be transferred via SSL. This is an effective and easy way to limit cookie transmission to encrypted channels. The problem with this is that if a user visits an unencrypted page they will appear to log out (as their cookie is not transmitted with their request). Unfortunately the only solution to this problem is to put your entire Drupal site behind SSL, which may have performance implications on high traffic sites.

To ensure that PHP enforces secure cookies per http://www.php.net/manual/en/session.configuration.php#ini.session.cookie-secure you can alter the settings.php page of your drupal installation to include the following:

ini_set('session.cookie_secure', 1);

Another useful option is to set the "HTTP only" cookie setting. This setting prevents JavaScript from accessing the cookie an prevents cookie theft via cross site scripting (XSS) attacks. To do this include the following in your settings.php file:

ini_set('session.cookie_httponly', 1);

Persistent Threats

Once you've ensured that users have relatively secure accounts (with good passwords that aren't sent over e-mail, brute force detection and prevention, and secured connections for credentials) your Drupal installation will be in much better shape than with the default configuration. The final threat to user account security is that presented from a cross site scripting (XSS) attack agains the user's password. Unfortunately Drupal does not require that a user enter their old password before changing it to a new value. This means that an attacker can create a malicious piece of JavaScript that changes the users password and insert it on a page using any XSS vulnerability in the site (ref: http://www.madirish.net/?article=239). The best defense against this sort of attack is judicious examination of modules before installation and paying close attention to module upgrades that prevent XSS vulnerabilities. It is also wise to utilize the Drupal syslog module so that logs of activity are sent to the system syslog facility. Once this is in place it is possible to monitor the syslog and alert on user password changes. This is particularly valuable when you create alerts for changes to administrative accounts. Although it won't prevent an attacker from exploiting this sort of attack, it will provide quick notification and hopefully remediation.

Account Permissions

Account security is only one of the building blocks towards a secure Drupal installation. Because there is a constant threat of account compromise it is important to carefully limit permissions within your Drupal site. While some permissions are explicitly called out as dangerous in the permissions page, such as the PHP permission, the Drupal security team has released alerts (http://drupal.org/node/372836) about the potential dangers that some less obvious permissions present. One avenue of exploitation that malicious users (or attackers who compromise user accounts) will take is to attempt to craft PHP that will enable a back door to take control of the web server. Unfortunately Drupal offers many opportunities for users to craft PHP from within the web interface. The Drupal core 'PHP filter' module should be disabled unless it is in active use. Even better is to delete the entire directory from within the Drupal installation (in modules/php). This prevents the creation of a PHP enabled input type that could be exploited by an attacker. Unfortunately even with this module entirely removed from an installation there are still ways for attackers to execute PHP. Most modules check to make sure that the PHP filter is enabled before allowing PHP in input, but some modules ignore this prerequisite setting. One core module that allows for bypassing input restrictions is the block module. If a user has permissions to administer blocks and 'use PHP for block visibility' they can execute PHP by placing it in the "Pages:" textarea under the 'Page specific visibility settings' form field on the Administer -> Site building -> Blogs page if the proper radio button is checked. If a user, enters say:


<?php touch('/var/www/html/drupal-6.15/sites/default/files/foo.txt'); return true;?>

That code will be executed silently every time Drupal is viewed.

Becuase PHP code execution is so prevelant in Drupal installations, and is pretty much impossible to remove through simple configuration, it is absolutely critical to limit permissions to the smallest subset of users as possible. Creating separate accounts for site administration and site use is advisable. Don't use the same account to configure your site, doing things like editing menus and configuring views, as well as to perform day to day operations such as creating content and administering user comments. By separating privilege you lower the chances that a hijacked account will be able to cause harm. Because PHP operates with the same system privilege as the web server, a compromised user account that can access PHP means that the web server is compromised.