Web server security

Where web servers are concerned, there are many options to improve the security of connections, but also to prevent cross-site attacks.

SSL

Simple SSL/TLS configurations are shown here first, followed by expanded SSL/TLS features, such as HSTS and OCSP. In principle, it is always recommended that the current protocols and cipher suites are copied, for example from the Mozilla Wiki. These are frequently updated and not only list different cipher suites, but also provide the relevant compatibility list.

The “Modern” configuration is desirable, but often means that older clients can no longer connect to the servers. It is therefore recommended that the “Intermediate” configuration is used in preference. The configurations of different softwares are also always provided in the Mozilla Wiki and Cipherli.st While Cipherli.st may be structured more clearly and show more examples, the cipher suites are nowhere near as well thought out as in the Mozilla wiki. For this reason, it is better to use the cipher suites from the Mozilla wiki.

Nginx

server {
listen 80 default_server;
listen [::]:80 default_server; # Redirect all HTTP requests to HTTPS
return 301 https://$host$request_uri;
} server {
listen 443 ssl http2;
listen [::]:443 ssl http2; # Certs sent to the client in SERVER HELLO are concatenated
ssl_certificate /path/to/signed_cert_plus_intermediates;
ssl_certificate_key /path/to/private_key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off; # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /path/to/dhparam.pem; # Intermediate configuration, tweak to your needs
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000; 
# OCSP Stapling
# Fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on; 
## Verify chain of trust of OCSP responses
ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates; 
resolver <IP DNS resolver>; 
[...]
}

As a CA certificate (ssl_certificate parameter), nginx expects the certificate of all intermediate CAs and the certificate of the own server (all in PEM format). They can simply be copied together (the first certificate in the file should be the server certificate).

Apache

<VirtualHost *:443>
[...]
SSLEngine on
SSLCertificateFile /path/to/signed_certificate_followed_by_intermediate_certs
SSLCertificateKeyFile /path/to/private/key 
# Uncomment the following directive when using client certificate authentication 
#SSLCACertificateFile /path/to/ca_certs_for_client_authentication
# HSTS (mod_headers is required) (15768000 seconds = 6 months)
Header always set Strict-Transport-Security "max-age=15768000"
[...]
</VirtualHost> 
# Intermediate configuration, tweak to your needs
SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
SSLHonorCipherOrder on
SSLCompression off
SSLSessionTickets off # OCSP Stapling, only in httpd 2.3.3 and later
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:/var/run/ocsp(128000)

Testing

Qualys SSL Labs is best suited for testing HTTPS servers. Tests which test the servers for vulnerabilities can be incorporated quickly, even in case of new attacks. Hardenize can also be used as an alternative. However, this is currently still in a test phase.

It is possible to carry out manual tests with openssl, the following test can be easily extended with own parameters. This is only a basis command, which displays the contents of the server certificate in text form. It can also be used to test the cipher suite, but since this involves long and numerous commands, it is recommended to use the existing services for this purpose. If this is not desirable or technically feasible, SSL Decoder, for example, can be installed and run locally, the sources for this are provided on GitHub.

$ openssl s_client -connect example.com:443 2>/dev/null </dev/zero | openssl x509 -noout -text

Additional parameters are documented in the man page of s_client and x509.

Other features in relation to SSL/TLS

Web servers can also send headers for Strict-Transport-Security and Public-Key-Pins. It depends on the application which headers are required and which are not.

Strict-Transport-Security – RFC 6797

The concept is that a browser which was already connected to a website knows that HTTPS should always be used in future. The value which is sent by the web server is the number of seconds for which this setting is to be valid.

Public-Key-Pins – RFC 7469

With Public Key Pinning (HPKP), the Subject Public Key Info is specified in the certificate (is already in the Certificate Signing Request), which the web server must issue. With the increasing proliferation of Let’s Encrypt this is no longer as simple as it used to be, as certificates are automatically renewed there. HPKP works if the same Certificate Signing Request is used all the time, but this must be implemented by the Let’s Encrypt client accordingly. Understanding HPKP is extremely important to implement this correctly. If mistakes are made, it may not be possible for certain clients to use websites for longer periods. Backup keys are mandatory and are also expected by the RFC. Of course, server backups must not be forgotten either.

Further information

Web servers should also issue specific headers, e.g. against cross-site scripting. It depends on the application which headers are required and which are not. securityheaders.io offers the possibility to test common headers:

  • Content-Security-Policy
  • X-Frame-Options
  • X-XSS-Protection
  • X-Content-Type-Options

These options can have an impact on the website and some require specific adjustments.

A possible configuration of nginx looks as follows (in the server section):

add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload";
add_header Content-Security-Policy "script-src 'self'";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";

The corresponding values are configured by default and should be adjusted so that the website is functional. Common browsers display the items which were blocked when the page is loaded in the developer tools. This makes it possible to determine the values which still need to be added.

Links