Drupal and Apache Web Site Security Checklist, part 3

Topics: Apache, Drupal
Technologies: Apache 2+, Drupal 5+, PHP 5+

Default Drupal, Apache, and PHP settings broadcast to hackers much more about your site's configuration than hackers should know. In this third article in a series, I review settings to tighten security and reduce this information leakage.

This is part 3 in a series of web site security checklists. Part 1 reviewed Apache and Drupal settings to restrict access to files and directories, and Part 2 reviewed Drupal settings to control access to content in Drupal's database.

Introduction

In a 2009 report (PDF), White Hat Security reports the top ten vulnerabilities in sites they tested. Number two, with a 47% likelihood, is Information Leakage. Vulnerabilities of this type come in many forms. They needn't just include obvious problems, like posting credit card and social security numbers to public pages. Other information leakage problems reveal information about the site's software and configuration, such as the model and version number of the software, host names, IP addresses, logins, and passwords.

Web masters often rely on "security through obscurity" — hoping that hackers won't notice security flaws. But today's hackers have automated attack tools that scan sites for vulnerabilities, poking at everything in the hope of finding a weakness to exploit. Relying on obscurity is not sufficient.

In this article I review more Apache, PHP, and Drupal settings you can use to reduce information leakage about the software and configuration you use. The less you tell hackers about your site's workings, the better.

Let me repeat from the first article:

  • I do not discuss downloading or installing software or setting up directories, permissions, and chroot. How you do these is important, but beyond the scope of this article.
  • I do not discuss basic Apache, PHP, MySQL, and Drupal configuration. I assume that you've already got things working and now you're ready to tighten security.
  • This list is not exhaustive. No list could be.
  • All of these settings have been tested, but test them yourself before using them on a production site. Every site is different.

Reduce published software names and version numbers

Don't tell hackers anything you don't have to. Disable everything you can that reveals the software you're using, its version numbers, and its configuration.

Minimize Apache server information in HTTP messages

By default, Apache includes its name and version number in the "Server" field in every HTTP response. Here is a typical output (you can use Web-Sniffer.net to view HTTP responses from a site):

Server:  Apache/2.2.9 (Unix) PHP/5.2.6

Reduce this to the product name only by setting the "ServerTokens" directive in your "httpd.conf" file. In default Apache configurations, this is in "extra/httpd-default.conf".

ServerTokens Prod

This reduces the output to just:

Server:  Apache

Disable or restrict Apache server information pages

Apache's mod_info module can display a page listing your server's full configuration. It is often served at "http://YOURSITE/server-info". To disable it, look for a "SetHandler server-info" directive in a "<Location>" group in your configuration files and comment out the group. In default Apache configurations these directives are in "extra/httpd-info.conf" and included by "httpd.conf". Comment out the include directive to disable it all.

# Don't show server info
#Include extra/httpd-info.conf

Alternatively, if you must have this information, then limit access to only trusted hosts by adding "Allow" directives.

# Allow access to server info from server and local network only
<Location /server-info>
     SetHandler server-info
     Order allow,deny
     Allow from 127.0.0.1   # Localhost
     Allow from 192.168.    # Local network (use your local IP range)
</Location>

Restrict Apache server status pages

Apache's mod_status module can display a page listing all the running instances of the server and what they are doing right now. This list is often served at "http://YOURSITE/server-status". To disable it, look for a "SetHandler server-status" directive in a "<Location>" group in your configuration files and comment out the group. In default Apache configurations, these are in "extra/httpd-info.conf" and included by "httpd.conf". Comment out the include directive to disable it all.

# Don't show server status
#Include extra/httpd-info.conf

Alternatively, if you need this information, then block access to it from anywhere except trusted hosts by adding "Allow" directives.

# Allow access to server status from server and local network only
<Location /server-status>
     SetHandler server-status
     Order allow,deny
     Allow from 127.0.0.1   # Localhost
     Allow from 192.168.    # Local network (use your local IP range)
</Location>

Disable PHP information in HTTP messages

By default, PHP contributes its name and version number to Apache's "Server" field in every HTTP response. It also adds an "X-Powered-By" field and again includes the PHP version number. Here's a typical value:

X-Powered-By:  PHP/5.2.6

Disable this by disabling "expose_php" in your "php.ini" file.

expose_php = Off

Disable the Apache server signature

By default, Apache includes information about the server and web master in its error pages. Disable this by disabling "ServerSignature" in your configuration files. In default Apache configurations this is in "extra/httpd-default.conf".

ServerSignature off

Change the default name of the PHP session cookie

PHP provides built-in management of a session cookie. This is used by Drupal, and a lot of other PHP code. Unless set explicitly, the cookie name is "PHPSESSID". Avoid advertising that you're using PHP by changing the default name to something generic, like "Session", by setting the "session.name" value in your "php.ini" file.

session.name = Session

Or set the value in your site's ".htaccess" file.

php_value session.name Session

Or set it for an individual Drupal site by adding it to "sites/YOURSITE/settings.php".

ini_set('session.name', 'Session');

Remove pages that display "phpinfo( )"

PHP's "phpinfo" function shows detailed information about your PHP configuration. Never display this output on a public page. Scan your site's pages for the call, or use a search engine to look through your site for pages containing the function's output phrase "This program makes use of the Zend Scripting Language Engine" (include the double-quotes). You'd be surprised by how many public sites have this information published.

Remove content that shouldn't be served

Default distributions of Apache and Drupal include files there is no need to serve. When served, they can tell hackers exactly what software and versions you're using. Either block serving them, or move them to a private unserved directory.

Block Apache from serving its manual

Apache distributions include full documentation and an "extra/httpd-manual.conf" file that places that manual on-line at "http://YOURSITE/manual". Since the manual is on-line at Apache's site anyway, there is no need to serve it yourself, or indirectly tell hackers exactly what version of Apache you are using. Comment out the include directive for "httpd-manual.conf" in your "httpd.conf" file.

# Don't serve the Apache manual
#Include extra/httpd-manual.conf

Move Drupal's text files

Drupal's installation includes helpful text files in the top-level directory, such as "INSTALL.txt", "UPGRADE.txt", and "CHANGELOG.txt". By default, all of these files are served at "http://YOURSITE/INSTALL.txt" et al. Nobody but you needs to see them, so move them to an unserved directory.

Move Drupal's script files

Drupal's top-level "scripts" directory contains optional shell and perl scripts that help clean up code or run Drupal's "cron.php" automatically. These scripts should never be served. Move the entire directory to an unserved directory. In fact, avoid ever having a directory with a name like "scripts" — it is a standard target for hacker scripts that probe a site. Other obvious targets are "cgi-bin", "bin", and "drupal". Watch your server logs and you'll soon see automated attacks trying these and other common directories.

Move Drupal's "install.php"

Drupal's "install.php" in the top-level directory is used once during the installation of Drupal. It is never used again, so there is no need to keep serving it. Move it to an unserved directory.

Remove extra entries from Drupal's "robots.txt"

Drupal's "robots.txt" file requests that search engines skip indexing certain files and directories. After you've moved the above text files and scripts out of the Drupal site directory, remove lines for them from "robots.txt". There is no need to advertise that the files ever existed.

One site security package I tested looks for "http://YOURSITE/update.php" to determine if a site is running Drupal. If found, it parses "http://YOURSITE/CHANGELOG.txt" to get the Drupal version number. Unfortunately, "update.php" does need to be served – but it doesn't have to be served to everybody. In part 1 of this article series I discussed ways to block access to this and other Drupal files.

Hide revision control comments

For added paranoia, remove revision control system comments added into served Drupal files. These comments give strong clues about the version of Drupal you're using.

Remove Drupal's version number from "robots.txt"

Drupal provides a "robots.txt" file for the site's top level directory. The first line of the file includes a revision control system comment and version number. Use a text editor and remove it.

Remove Drupal's version numbers in CSS files or enable CSS aggregation

Most of Drupal's CSS files include a revision control system comment that gives a version number. Either use a text editor to remove it, or enable CSS aggregation on Drupal's Administer > Site configuration > Performance page. CSS aggregation strips comments as it combines CSS files into one larger file.

Remove Drupal's version numbers in Javascript files or enable CSS aggregation

Like Drupal's CSS files, Drupal's Javascript files also include a revision control system comment on the first line. Edit those files to remove it, or enable Javascript aggregation on Drupal 6's Administer > Site configuration > Performance page. This combines Javascript files and strips out those comments.

If you don't use Javascript aggregation, consider minifying your Javascript to strip out comments and make the code smaller. Otherwise, use a text editor and remove the version number comment at the top of each file.

Remove content that announces what software you use

While you can't remove all evidence of what server-side software you're using, you can be less obvious about it.

Site brag badgesRemove "badge" images and configuration bragging

Image badges that declare loyalty to Drupal/MySQL/PHP/Apache/Linux/Mac OS X tell hackers how to attack. Remove the badges and any posts that brag about the site's server configuration.

Use a custom favicon instead of Drupal's favicon

Drupal's default configuration provides its own blue-water-drop favicon. Instead of advertising on every page that you are using Drupal, create your own favicon. Select the icon on Drupal's Administer > Site building > Themes pages.

Drupal's default configuration also uses Drupal's own logo as your site logo for all themes. Change this to something more appropriate on Drupal's Administer > Site building > Themes pages.

Drupal's default icon is available at "http://YOURSITE/misc/durplicon.png", even if you don't use it as your site's logo. That makes it an easy way to test if your site is running Drupal. This file, and several other Drupal files should be served only when attached to pages at your site. I discuss ways to block access to these files in part 1 of this article series.

Disable error and debugging information

Avoid advertising software bugs and the inner workings of your site. Block error and debug messages.

Disable PHP on-page errors

By default, PHP errors print messages onto the web pages returned to users. Disable on-page error messages by unsetting "display_errors" and "display_startup_errors" in your "php.ini" file. Enable writing errors to your site's log instead by setting "log_errors" in your "php.ini" file.

display_errors = off
display_startup_errors = off
log_errors = on

If you do not have access to the host's "php.ini" file, then you can add similar lines to your ".htaccess" file:

php_flag display_errors off
php_flag display_startup_errors off
php_flag log_errors on

Or you can set them in "sites/YOURSITE/settings.php" for Drupal:

ini_set('display_errors', 'off');
ini_set('display_startup_errors', 'off');
ini_set('log_errors', 'on');

Disable Drupal's on-page errors

By default, Drupal errors are reported to Drupal's log and printed onto the offending web page. Disable Drupal's on-page error messages in Administer > Site configuration > Error reporting.

Disable Drupal's devel module

Drupal's devel module is useful during development. It displays the queries executed and memory used at the bottom of each page for all user roles with the "access devel information" permission. Before the site goes into production, either disable the module or disable the "access devel information" permission for all user roles. Disable the module on Drupal's Administer > Site building > Modules page or change its permissions on the Administer > User management > Access control page.

Disable Apache TRACE responses

The HTTP TRACE request is a debug feature that echos back to the sender whatever was sent to the server, including cookies. It can be used by browser Javascripts to bypass browser security and access cookies, including authentication credentials. In Apache versions prior to 2.0.55, disable trace responses with mod_rewrite directives explained on Apache's news page. In newer versions of Apache, use "TraceEnable" in your "httpd.conf" file.

TraceEnable off

Further reading

Related articles at NadeauSoftware.com

Web articles and specifications

Books

Comments

Remove content that announces what software you use

Hi,

Very interesting article.

One comment though to help hiding Drupal: usage of CSS + JS aggregation, apart the performance improvements, will result in hashing Drupal specific JS and CSS file names and therefore masquerading them in the pages source code. For example:

<script type="text/javascript" src="/misc/drupal.js"></script>

will get hashed into:

<script type="text/javascript" src="/files/js/MD5_HASH.js"></script>

But still, one is able to parse the JS file code and detect the $Drupal object.

Re: Remove content that announces what software you use

Right. Aggregated CSS and JavaScript files use MD5 hash code names, and thereby hide "drupal.css" and "drupal.js". However, this doesn't change the names of functions and CSS styles. So, looking for distinctive Drupal names in the code will still detect if Drupal is present.

In the bigger picture, there's nothing you can do to completely hide Drupal, PHP, or Apache. They all have visible properties that cannot be hidden. Drupal's "robots.txt" file, for instance, must be publicly available for it to work. But a quick look at it reveals distinctive Drupal-isms. For Apache, even when server tokens are set to a minimum, Apache still announces itself as "Apache" in every HTTP header.

The idea, though, isn't to completely hide Drupal, PHP, or Apache, but rather to make it less obvious and not reveal version numbers. Hackers will still attack and still run scripts that try lots of known vulnerabilities. But if those scripts can't figure out exactly what you're using, they won't be as effective.

Excellent guide, thank you.

Excellent guide, thank you.

You should also mention, if users find any security holes in the Drupal core or contrib modules, then they should not post it on the issue queue but to contact the security team: security@drupal.org

Cheers,

Change HTTP header "Expires: Sun, 19 Nov 1978 05:00:00 GMT"

As described here:
http://www.lullabot.com/articles/is-site-running-drupal
Workaround: hack functions 'drupal_page_header' and 'drupal_page_cache_header' to change hard coded values.

Re: Change HTTP header "Expires: Sun, 19 Nov 1978 05:00:00 GMT"

While Drupal may use the above HTTP "Expires" value for its pages, that value is in no way unique to Drupal. It will likely have a very high false-positive rate (identifying non-Drupal sites as running Drupal), and that makes it a poor signature for hacking tools.

There are so many better Drupal signatures that worrying about this one is probably overdoing it. Drupal's "robots.txt" file is a much better signature, or "drupal.js" and "drupal.css" files or their contents. Worry about those first, if you feel like worrying.

Access to php.ini

# Block access to php.ini by putting this in .htaccess

Order allow,deny

Re: Access to php.ini

First, you should never have "php.ini" or any other Apache or PHP configuration file within a served directory. All configuration files should be far away, outside of all served directories, owned by a system administrator account, and locked without write permissions by anybody else. Once out of a served directory, you never have to fiddle with Apache directives to protect it.

Second, the Apache line you propose blocks all access to all files, including the files you want to give access to. If you're going to block access, you need a "Files" or "Directory" directive to control what exactly you're blocking.

Drupal and Apache Web Site Security Checklist

Very good informative list, including many aspect of the CMS particle security issues. thanks!

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

Nadeau software consulting
Nadeau software consulting