Quick Guide – Hardening Apache2

This is a quick overview of a secure Apache2 configuration. We won’t be going into Linux hardening, but will focus instead on the basic configuration options of Apache2. The following code boxes show examples of a secure configuration. Please adjust them to your requirements and test each of them before applying them to a live/production system.

The examples below include the web server miss-configurations and weak spots that we most commonly observe during our web applications pentests.

Prerequisites

Before we start changing anything, here is a list of Apache2 modules that are required for these settings. I’ve added comments in the code boxes as to which module is required, so just enabled them according to your requirements.

sudo a2enmod headers
sudo a2enmod ssl
sudo a2enmod proxy proxy_html

While enabling modules requires Apache2 to be restarted, a reload of the config files should suffice for changes in the configuration.

# for modules
sudo service apache2 restart

# for configuration changes
sudo service apache2 reload

Prevent Information Disclosure

Apache2, by default, is rather talkative about its own version. If you hit a 404 site that’s been generated by the server, it usually includes some information about the web server and its version. This is information that could potentially help attackers find weak spots in your server. It’s easy to disable though and is, as far as my experience goes, risk free.

sudo vi /etc/apache2/conf-enabled/security.conf
# Set the following options/values to turn off information disclosure
ServerTokens Prod
ServerSignature Off
TraceEnable Off
Header unset Server
Header unset X-Powered-By

Note that the “Header unset Server” option will still sometimes show the “Server: Apache” header, for example in Apache’s 404 responses. Apache2 itself doesn’t offer an easy way to get rid of these, but the associated risk isn’t all that high either. If you insist on removing it, I suggest using either the module ModSecurity to remove the header completely, or hiding the web server behind a load balancer, such as HAProxy, which offers the same functionality.

While we’re at setting header options, let’s set a few more to increase the security of the web application as well. In the example above we’ve unset headers to remove sensitive information from our HTTP requests. In the next section we will set headers instead, to add security instructions for browsers.

Header set X-Content-Type-Options: "nosniff"
Header set X-Permitted-Cross-Domain-Policies: "master-only"
Header set X-XSS-Protection: "1; mode=block"

# Only set this option if you are not embedding this page somewhere else (e.g. in Owncloud or iFrames on other sites!) 
Header set X-Frame-Options: "sameorigin"

This is what I usually set by default on all my servers. The first three are absolute no-brainers and have never cause any problems for me in the past. The X-Frame-Options header should be used to prevent click-jacking attacks, unless your application has to cooperate with one of your other servers, in which case this header could cause some trouble.

For more information, and a quite handy check of your servers header settings, check out securityheaders.io.

File Permissions

This is technically not an Apache2 configuration but important nevertheless and since Apache2 already runs under its own user, www-data, might as well set the permissions right.

Obviously, file permissions should always be set according to the least-privilege principle. This means that the files should only be accessible by whoever needs to access them and permitted actions (read,write,execute) should be limited as much as possible.

So depending on your web application, you should start with the following settings and work your way up the permission chain, until everything is working.

# Change owner and group of DocumentRoot to www-data recursively (www-data is the Apache2 user on Ubuntu)
$ sudo chown -R www-data:www-data /var/www/yourapp/

# Set default permissions for everyone recursively
# Note that www-data doesn't always need write permissions, try 440 first to tighten permissions even more.
$ sudo chmod -R 640 /var/www/yourapp/
# user=read/write, group=read, other=none

# Set execute rights for user and group to folders (required) and files that where executable before
# Note the capital X - lower case x would set execute permissions for every file instead!
$ sudo chmod -R u+X /var/www/yourapp/
$ sudo chmod -R g+X /var/www/yourapp/

Please note that setting the permissions above can cause problems with delivering your website, so don’t apply them to your production system without testing the application beforehand in a safe environment!

Here is an example with a fresh DokuWiki installation. Note that DokuWiki is rather easy to handle in regards to file permissions. Other applications might require more work to be fully functional.

$ sudo ls -lah dokuwiki/
total 92K
drwxr-x---  8 www-data www-data 4.0K Sep 18 11:11 .
drwxr-xr-x 15 www-data www-data 4.0K Sep 18 11:10 ..
drwxr-x---  2 www-data www-data 4.0K Sep 18 11:10 bin
drwxr-x---  2 www-data www-data 4.0K Sep 18 11:10 conf
-rw-r-----  1 www-data www-data  18K Sep 18 11:10 COPYING
drwxr-x--- 12 www-data www-data 4.0K Sep 18 11:11 data
-rw-r-----  1 www-data www-data 3.6K Sep 18 11:10 doku.php
-rw-r-----  1 www-data www-data  19K Sep 18 11:10 feed.php
-rw-r-----  1 www-data www-data 1.5K Sep 18 11:10 .htaccess.dist
drwxr-x---  7 www-data www-data 4.0K Sep 18 11:10 inc
-rw-r-----  1 www-data www-data  182 Sep 18 11:10 index.php
drwxr-x---  8 www-data www-data 4.0K Sep 18 11:10 lib
-rw-r-----  1 www-data www-data  306 Sep 18 11:11 README
drwxr-x---  5 www-data www-data 4.0K Sep 18 11:10 vendor
-rw-r-----  1 www-data www-data   23 Sep 18 11:11 VERSION

$ ls -lah dokuwiki/
ls: cannot open directory dokuwiki/: Permission denied

Since I’m not working as www-data user, I don’t have permission to access the files. However, www-data does have the required permissions to access and write the files, therefore the website is being delivered properly and I have no problem creating new entries.

Default SSL/TLS vHost

Terminology: Just to avoid confusing anyone and to keep this simple. There are two protocols used for HTTPS, both are available in different versions. SSLv2 and SSLv3 have known vulnerabilities, are considered insecure and should therefore not be used any more. TLS comes in three versions, 1.0, 1.1 and 1.2 and is the successor of SSL. Each TLS version tries to do better than the last, but server and browser side adoption is slow and therefore all three versions should be used for now. You will still see the term SSL everywhere though, as most configuration options and tools are still named after it.

Encrypting the traffic from and to your web server is more and more becoming the norm, which is good, as offers great security – if done right. The configuration example below should help you set up HTTPS properly.

<VirtualHost IP:443>
ServerAdmin admin@lastbreach.com
ServerName yourserversname

# Disable DocumentRoot if Proxy is used! (see below)
DocumentRoot /var/www/yourapp/

# SSL/TLS Settings - requires module "ssl"
SSLEngine on
SSLCertificateFile /etc/ssl/private/ssl-certificate.crt
SSLCertificateKeyFile /etc/ssl/private/ssl-certificate.key
SSLCACertificateFile /etc/ssl/private/cacert.pem

### Reverse Proxy Settings, requires modules "proxy" and "proxy_http"
### Apache can act as SSL/TLS proxy for your HTTP only applications.
#SSLProxyEngine On
#ProxyPreserveHost On
#ProxyRequests Off
#ProxyPass / http://host:port/
#ProxyPassReverse / http://host:port/

ErrorLog "|/usr/bin/logger -p local2.error -t apache"
CustomLog "|/usr/bin/logger -p local2.notice -t apache" common

# Header Settings - requires module "headers"
# If you use HTTPS and have a valid certificate, enabling HSTS is strongly advised! (15768000 seconds = 6 months)
Header always set Strict-Transport-Security "max-age=15768000"
# Depending on your setup, you might even want to go further than that and choose the following Header options instead.
# Header always set Strict-Transport-Security "max-age=15768000; includeSubdomains; preload"
# Note that includeSubdomains can cause trouble, if your subdomains are not running on HTTPS, use mixed-content or don't have a valid SSL/TLS certificate!
</VirtualHost>

The HSTS header is a huge addition to your visitors security if your website runs on HTTPS, however, it can cause trouble if you are using mixed content. So test this header before rolling it out, especially if your website loads content from both HTTPS and HTTP sources. More info on the HSTS preload option can be found on the hstspreload website.

Proper TLS Configuration

TLS needs more than just a certificate to be secure. The TLS configuration options described below can be appended to following file.

/etc/apache2/conf-enabled/security.conf

The configuration described below is deemed secure (2015-09-18), lowering it in favour of compatibility (looking at you, Windows XP/IE6.0) will put all of your users at risk! The option most prone to updates is probably the SSLCipherSuite selection. For an updated selection of secure ciphers, please refer to the Mozilla SSL Config Generator.

# Disable the old and weak/broken protocols SSLv2 and SSLv3. Instead, the web server will use TLSv1.0, TLSv1.1 and TLSv1.2
SSLProtocol all -SSLv2 -SSLv3
# Define secure list of availabel Ciphers to choose from
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
# Honor = Choose Web Server Cipher choice over clients preferences (http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslhonorcipherorder)
SSLHonorCipherOrder on
# Disable SSL compression to avoid CRIME attack - http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslcompression
SSLCompression off

# OCSP stapling allows clients to check a certificates revocation status - https://en.wikipedia.org/wiki/OCSP_stapling
# OCSP Stapling, is only available in Apache 2.3.3 and later
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:/var/run/ocsp(128000)

You should test your configuration afterwards using either a current version of sslscan which is available for Windows and in most Linux repositories, most notably KALI,

sslscan --no-failed yourserver.com

or by heading over to Qualys SSL Labs for a detailed online report. Qualys will also show you what devices and browsers are compatible with your TLS settings.

These are good default settings for TLS but there is more. If you want to really ensure secure connections, take a look at HTTP Public Key Pinning (HPKP).

Disable HTTP and Port 80

Now that your website securely runs on HTTPS, you might want to think about disabling HTTP and port 80. Of course, public websites will keep HTTP running, if not for actual content than at least to redirect incoming visitors to HTTPS. But for internal applications that run on HTTPS only, disabling all port 80 vHosts, as well as the port 80 listener can be an improvement to security.

First, make sure to disable all vhosts in

/etc/apache2/sites-enabled/

that start with the following configuration.

<VirtualHost *:80>

Note that the * can be an actual star or something else, depending on your setup. You can either disable them by adding # at the beginning of each line or by disabling the whole file. The latter should obviously only be used, if you keep a separate file for each virtual host.

sudo a2dissite vhostconfigfilename

Once you’ve disabled all HTTP vhosts, you might as well disable port 80 all together to prevent someone accidentally enabling a port 80 vhost again.

sudo vi /etc/apache2/ports.conf
# disable Listen 80 if your server runs on HTTPS only. This is useful for internal web apps, but usually not used by public websites. 
# Listen 80

Make sure to restart Apache2 afterwards and check with the following command if Apache2 is still listening on port 80.

$ sudo netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      414/sshd        
tcp6       0      0 :::22                   :::*                    LISTEN      414/sshd        
tcp6       0      0 :::443                  :::*                    LISTEN      20646/apache2

That's it!

That should cover the most common miss-configurations. If you’ve gone through all that and applied the settings according to your environment and your requirements then you should be good to go – provided you patch your systems regularly of course!