SSL certificate security has never been more important than it is today. Browsers have begun to show users a warning on sites that don’t use HTTPS, more confidential information than ever is being communicated via web applications, and data-snooping criminal activity continues to grow year over year.

As such, today I am going to walk you through the process of hardening your server’s SSL certificate security via NGINX configurations. To do this you will need a server with:

    • Ubuntu v16.04 or greater (required for http2)
    • OpenSSL v1.0.2 or greater
  • An SSL certificate already installed

Okay – let’s begin!

Basic Setup

Though you are likely already doing this, first let’s make sure you are:

    • Forwarding all traffic from port 80 to port 443 (the HTTPS protocol’s port)
    • Using http2
  • Using your SSL certificate

To forward all traffic to port 443, modify your /etc/nginx/sites-available/default file so that it reads:

server {
  listen 80 default_server;
  listen [::]:80 default_server;
  # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
  return 301 https://$host$request_uri;
}

Because this file is imported in your /etc/nginx/nginx.conf it will be applied for all requests to all domains that your server manages, so long as you have it symlinked in /etc/nginx/sites-enabled.

To use http2 and your SSL certificate for a specific domain, modify your etc/nginx/sites-available/YOUR_FILE (could be default if you’ve kept things simple) so that it reads:

server {
  # SSL configuration
  # Use of default_server here is optional, but makes explicit this is the default
  # server for this port
  listen 443 ssl http2 default_server;
  listen [::]:443 ssl http2 default_server;
  server_name YOUR_SERVER_NAME;

  ssl_certificate PATH_TO_YOUR_CERT;
  ssl_certificate_key PATH_TO_YOUR_CERT_KEY;

  # You may have also had “ssl on;” in this file - specifying “listen 443 ssl”
  # above has the same effect so you can remove “ssl on;”

  # You should disable gzip for SSL traffic if you have it enabled to avoid the BREACH vulnerability

  # The remainder of your server config goes below
}

Enable HTTP Strict Transport Security (HSTS)

By default, when modern browsers detect HSTS headers they automatically stop attempting to use anything but HTTPS for the length of time specified in the header. We use this in conjunction with the port redirecting that we have already configured so that normal users will receive the proper idiomatic HSTS headers, and bad actors will be forcefully redirected.

Update /etc/nginx/nginx.conf’s http {} block to contain:

# Add HSTS header to all requests to force browsers to use HTTPS
# Do "max-age=15768000; includeSubDomains" instead of just "max-age=..." if using subdomains
add_header Strict-Transport-Security "max-age=15768000" always;

Restrict Protocols and Ciphers

Now, let’s make sure you are:

    • Prioritizing the SSL protocol TLSv1.3, with 1.2 as a fallback, and rejecting all others
  • Restricting the permitted ciphers

The following changes will all be made to your /etc/nginx/nginx.conf file’s http {} block because we want these changes to apply to all traffic, not just a particular server:

ssl_protocols TLSv1.2 TLSv1.3; # Prefer 1.3, fallback to 1.2, reject all others
ssl_prefer_server_ciphers_on; # Use the server’s cipher preference, not the client’s

# A list of all ciphers to permit, sorted most to least preferred. ! indicates ciphers to reject.
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4”;

ssl_session_cache shared:SSL:5m; # Share the cache with all worker processes across cores; Name the cache SSL; Set to 5 min
ssl_session_timeout 1h; # The length of time a client can reuse session parameters

Create A Stronger Diffie-Hellman

A Diffie-Hellman key is used for our SSL handshake with clients. By default, this key is 1024 bits. We want to make sure that if we are using a 2048+ SSL certificate we do not diminish its security by using a 1024 bit key during our key exchange/handshake. So, let’s create a stronger Diffie-Hellman key.

In the terminal, enter openssl dhparam -out /etc/ssl/dhparam.pem 4096 – this will take a while!

Then add the following lines to the /etc/nginx/nginx.conf file’s http {} block:

ssl_dhparam /etc/ssl/dhparam.pem; # Use our custom strong Diffie-Hellman for handshakes
ssl_ecdh_curve secp384r1; # Secure it with an elliptic curve algorithm instead of RSA

Confirm Everything Works

First, let’s make sure there are no syntax errors with our configurations:

sudo nginx -t

Now let’s run our configuration tests:

sudo /etc/init.d/nginx configtest

Then, restart NGINX:

sudo service nginx restart

Now that everything is configured, run the Qualys or Comodo testing suites and look at your fancy A+ score!

At Moove It we work to stay on top of the latest developments in online security to ensure that our clients and their data are kept safe in an increasingly complex and dynamic digital environment. I hope that this guide helps you to bring some of that same sense of security to your next application!

Sources:

https://ubuntu101.co.za/nginx/upgrade-nginx-1-12-ubuntu-16-xenial/

https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-with-http-2-support-on-ubuntu-16-04

https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html

http://nginx.org/en/docs/http/ngx_http_ssl_module.html#directives

Get our stories delivered from us to your inbox weekly.