blog/_posts/2017-11-14-hardening-apache...

8.6 KiB

title date last_modified_at url layout category image description
Hardening Apache, all in one place 2017-11-14 2022-09-04 hardening-apache-all-in-one-place post Security /img/blog/hardening-apache-all-in-one-place.png An unified guide to secure Apache web server

A missing blog post image

Oops, I did it again ! I should have said "httpd" of course, the famous Apache web server.

Introduction

I was so bored of starting from scratch for each new VHOST created, so the idea of writing an article keeping a template with the best security measures (taken from many places, see Sources below) has come up.
And here we are, a brand new Markdown post about it ! ๐ŸŽ‰

Let's start with the first part !

Part 1 - Securing Apache

If you have never secured Apache globally, you should start with this part (although, if you do, skip it and go to the second part) !

In a few words, the configuration below will... :

  • Block all requests (your VHOSTs will have to allow them explicitly on their side)

  • Turn off Apache verbosity

  • Block all requests to a hidden resource (handful for .git/ folder for instance)

  • Enforce OWASP headers recommendations

  • Authorize Cookies only on secured connections

  • Reduce the default timeout for sessions and limit requests size to 1 MB (you can increase it if you need to)

  • Force clients to negotiate only with secure ciphers, on TLS v1.2 (see Sources)

Just create this file :

# nano /etc/apache2/conf.d/security.local.conf

... and paste the following :

{% highlight apache %} AllowOverride None Require all denied

ServerTokens Prod ServerSignature Off TraceEnable Off FileETag None

RedirectMatch 404 "^./.(?!well-known).$"

Header always set Referrer-Policy: "same-origin" Header always set X-Content-Type-Options: "nosniff" Header always set X-XSS-Protection: "0" Header always set X-Frame-Options: "SAMEORIGIN" Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; frame-ancestors 'self'"

Header always edit Set-Cookie "^(.*)$" "$1;HttpOnly;Secure"

TimeOut 60 LimitRequestBody 1024000

SSLHonorCipherOrder On SSLProtocol -ALL +TLSv1.2 SSLCipherSuite EECDH+CHACHA20:EECDH+AESGCM SSLUseStapling On SSLStaplingCache "shmcb:logs/stapling-cache(150000)"

For Apache >= 2.4.3

SSLCompression Off

For Apache >= 2.4.11

SSLSessionTickets Off

BrowserMatch "MSIE [2-6]" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0 BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown {% endhighlight %}

You'll need some new modules to enforce these measures (and those during the second part) :

# a2enmod headers rewrite ssl

I'd advise you to catch a glimpse of the modules already loaded with :
# apachectl -M
You can easily remove some of them you won't use, as below :
# a2dismod cgi autoindex <...>

If you need any protection from DDoS or BruteForce attacks, I advise you to follow this tutorial.

Reload Apache to parse and apply the new configuration :

# systemctl reload apache2.service

Now, you're all set for the second part ๐Ÿ˜‰

Part 2 - VHOST specific configuration

Below, you'll find two configurations :

  1. The first one is supposed to be named 000-default.conf : It will listen to the port 80, and redirect to each HTTPS VHOST in function of the domain name requested (just complete it with as many RewriteCond as you need)

  2. The second one is the content you'll have to duplicate for each of your VHOST. They will listen to the port 443 (let's say with Let's Encrypt support).

{% highlight apache %} <VirtualHost *:80>

ServerName your.domain.name
ServerAlias www.your.domain.name
ServerAdmin webmaster@your.domain.name

LogLevel warn
ErrorLog ${APACHE_LOG_DIR}/000-default_error.log
CustomLog ${APACHE_LOG_DIR}/000-default_access.log combined

RewriteEngine On
RewriteCond %{HTTP_HOST} ^your.domain.name$ [OR]
RewriteCond %{HTTP_HOST} ^www.your.domain.name$
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
{% endhighlight %}

{% highlight apache %} <VirtualHost *:443>

ServerName your.domain.name
ServerAlias www.your.domain.name
ServerAdmin webmaster@your.domain.name

DocumentRoot /path/to/webroot/

<Directory /path/to/webroot/>
    Options -Indexes -FollowSymLinks -Includes -ExecCGI
    AllowOverride None
    Require all granted
</Directory>

Header always set Strict-Transport-Security: "max-age=63072000; includeSubDomains; preload"

RewriteEngine On
RewriteCond %{THE_REQUEST} !HTTP/1\.1$
RewriteRule .* - [F]

SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/your.domain.name/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/your.domain.name/privkey.pem

LogLevel warn
ErrorLog ${APACHE_LOG_DIR}/your.domain.name_error.log
CustomLog ${APACHE_LOG_DIR}/your.domain.name_access.log combined
{% endhighlight %}

This VHOST needs some explanations :

  • Set the webroot and harden the website authorization

  • Set HSTS header (see Sources)

  • Disable (only) HTTP 1.0 requests

  • Loads Let's Encrypt SSL certificate

    We should say "TLS", shouldn't we ? (Coucou Nico' ๐Ÿ‘‹)

  • Set dedicated logs (useful to debug and monitor which website is under attack)

Don't forget to enable each one of your websites with :
# a2ensite <website>

Note

You may encounter a side effect of your VHOST naming.
Try to access your web server directly with its IP, instead of one of the VHOST's domain names) : http://your.server.address.ip/.
It's very likely that you don't want this website to answer when crawlers will start dealing with your server.
To recover from this behavior, just rename the VHOST you want to answer by default to something like 001-your-domain-name.conf.
When your IP will be accessed on the port 443, Apache will just redirect the query to the first VHOST name in a "top-down" way.

Last words

If you want to use a development Framework, it will surely shipped with a .htaccess file. Don't forget to pass the AllowOverride to All to authorize your default settings to be overridden for a specific VHOST.
On the same idea, some of them (hello CakePHP ๐Ÿ‘‹) use symbolic link to enable their plugins. So, you may need to activate FollowSymLinks too...

It's good to secure your web server, but if you are hosting a PHP application for instance, it won't block any attack occurring at the "application level"...


So this is the end, I hope it helped you ๐Ÿ‘

Please do propose some changes if you think there is something bad (or something missing) ; I'll try to keep this article up-to-date in the future ! ๐Ÿ‘Œ

Sources

History of revisions

2022-09-04 - Drop support for Apache < 2.4 and replace _default_ by *
2022-01-27 - Disable legacy XSS filtering through X-XSS-Protection header
2020-03-16 - Due to this notice, cipherli.st has been replaced by safeciphers.org
2019-11-16 - always set/edit security headers
2018-11-24 - Grants access to special (not-hidden) /.well-known/ paths
2018-11-11 - Gets rid of CBC encryption mode in ciphers list
2018-10-12 - Adds a default Referrer-Policy header to the local security configuration
2018-08-01 - Replaces Order, Allow and Deny options by Require for Apache >= 2.4, in a BC way for previous versions
2018-01-14 - Fixes wrong HSTS header setting
2018-01-09 - Adds Content Security Policy header to security.local.conf
2017-11-21 - Adds SSLStapling and Internet Explorer specific directives