Hardening Web Servers (Using Apache as an Example)

Nowadays, operating systems are rarely attacked directly, because the current firewalls are too good, but through applications, the more (web) servers are attacked. Frequent gateways are e. g. badly maintained WordPress installations (if you don't have someone who checks daily if there are security relevant updates and installs them immediately, … or not updated PHP versions - please note that you are only allowed to connect systems that are maintained (by the manufacturer and you) to the network of the TU Graz and that we take badly maintained systems after warning and already hacked systems immediately offline.

Simple Hardening Measures

First of all, you should ask yourself whether the web server (even behind a web interface for configuring a printer, a web server is running!) should be accessible from the Internet or other institute networks at all - if not, then no activation should be requested for the server/IP address, then it is at least not accessible and attackable from the Internet.

Even though it is actually "security through obscurity", it is still recommended not to reveal e. g. the version of the web server etc., here illustrated by the example of Apache:

RequestHeader unset Proxy
RequestHeader unset Proxy_Host
RequestHeader unset Proxy_Port
RequestHeader unset Proxy_User
RequestHeader unset Proxy_Pass
RequestHeader unset Proxy_Password

ServerTokens ProductOnly
ServerSignature Off
TraceEnable Off

RewriteEngine on
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
RewriteRule .* - [F]

HTTPS

At least if you also transport user data to the server (input forms), then you should definitely use encryption (i. e. HTTPS), since 2017 most browsers classify web servers as insecure if they do not use HTTPS or only certificates issued with SHA-1 for form inputs, since the beginning of 2020 many browsers generally indicate that the connection is not secure for servers without HTTPS because the displayed data could also be manipulated.

However, encryption alone is not enough, it has to be trusted state of the art encryption and for that several things are necessary, including an official certificate and some important settings on the web server:

  1. The certificate must not be issued with SHA-1.
    If you are not sure, then call www.ssllabs.com, enter your web server there and then look what is written in the line "Signature algorithm" - if SHA1 is written there, you must request a new certificate! You can get the necessary CSR e. g. by the command openssl req -new -newkey rsa:2048 -nodes -out NAME.csr -keyout NAME.key -subj "/C=AT/O=Technische Universität Graz/CN=NAME.tugraz.at". Save the certificate you get from Sectigo as NAME.crt.
    All in all, you should try to achieve at least an "A" at the above SSL Labs, an "A+" is desirable.
  2. For the server NAME.tugraz.at you should only allow certain methods of encryption, especially no SSL but only TLS and also only the latest variants 1.2 and 1.3 (this may also require an update of the SSL library), furthermore you should only allow secure "cipher suites" (these define which algorithms may be used in which priority):
    • Key exchange
    • Authentication
    • Encryption
    • Hash function
    SSLEngine on
    SSLProtocol -all +TLSv1.3 +TLSv1.2
    SSLCipherSuite 'EECDH+AESGCM:EDH+AESGCM'
    SSLHonorCipherOrder on 
    
    Further settings can be found e. g. on cipherli.st, but you should check that your users are not locked out by this.
  3. HTTP should be permanently redirected to HTTPS so that old bookmarks or links cannot lead to insecure pages (HSTS):
    <VirtualHost *:80>
     ServerName NAME.tugraz.at
     Redirect permanent / https://NAME.tugraz.at/
    </VirtualHost>
    <VirtualHost NAME.tugraz.at:443>
     ServerName NAME.tugraz.at
     SSLCACertificateFile /pfad/zu/den/certs/geant.crt
     SSLCertificateFile /pfad/zu/den/certs/NAME.crt
     SSLCertificateKeyFile /pfad/zu/den/certs/NAME.key
     Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
     …
    </VirtualHost>

    geant.crt is only needed if it is not contained in NAME.crt.

  4. For the (mostly used) "OpenSSL" must be checked continuously whether there are serious security vulnerabilities (POODLE, Heartbleed, Shellshock, FREAK, BEAST, Logjam, …) and patches. For verification there is a test here.

CSP

With the following settings, however, you must check whether everything then still works (e. g. frames, SSI, external content, etc.) and otherwise comment out individual lines:

Header always set X-Frame-Options "sameorigin"
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Content-Type-Options nosniff
Header always set Content-Security-Policy "default-src 'mailto' 'self'"
If you revise your website, then it should be thought of that CSP should be activated in the future!

An alternative could also look like this, for example:

Header always set Content-Security-Policy "default-src 'mailto' 'unsafe-inline' 'self' www.youtube.com;"

OCSP stapling

Outside the <VirtualHost></VirtualHost> blocks: SSLStaplingCache shmcb:/tmp/stapling_cache(128000) and in each <VirtualHost></VirtualHost> block SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
(or in the tls.conf - see below).

HPKP

You should think very carefully about which servers you use pinning for, since it actually only helps if someone manages to get a false certificate for our domain from another CA (currently Sectigo in our case) in order to carry out a man-in-the-middle attack against TU Graz. If the configuration is incorrect (or if the CA changes), this can lead to clients that support this no longer connecting to the server.

The "pinning" itself is actually quite simple.

Refer(r)er-Policy

You can and should define when the "Referer header" is set - example: Header always set Referrer-Policy same-origin causes the referer to be set only within the same environment, which prevents tracking a user across multiple servers.

Feature Policy

With this new header you can define more much, e. g. Header always set Feature-Policy "geolocation 'self'"

More Information

BetterCrypto.org, Geek Flare, Mozilla, SSL-Config-Generator, Sysinfo.io, …

Under Can I Use… (→ Show all) you can check which clients you lock out if you no longer support certain versions of SSL/TLS.

Hints

You can simplify this - if you have a lot of virtual servers - by creating e. g. 2 files tls.conf and hardening.conf and then simply include them in each virtual server:

<VirtualHost NAME.tugraz.at:443>
 ServerName NAME.tugraz.at
 Include conf/tls.conf
 SSLCACertificateFile /pfad/zu/den/certs/geant.crt
 SSLCertificateFile /pfad/zu/den/certs/NAME.crt
 SSLCertificateKeyFile /pfad/zu/den/certs/NAME.key
 Include hardening.conf
 …
</VirtualHost>

Due to a bug in OpenSSL it is currently not possible to define different cipher suites in name based virtual servers, the first cipher suite in the config file is always used!

Further Information

STIGs (DISA / (DoD).
CIS Benchmarks.
Apache Security Hardening Guide.

Header-Checks Security Headers, SSL Labs (Qualys), ImmuniWeb.

Secure Applications

In addition to the requirement that the web server should be set up as securely as possible (a task that needs to be done on an ongoing basis, vulnerabilities are discovered all the time and then need to be closed), it is of course also important to make sure that the applications are implemented securely.
The OWASP maintains 2 lists for the most exploited vulnerabilities:

Every new application should be checked against these vulnerabilities.