My little Poney

2014 has been an annus horribilis (yes, with 2 « n ») for SSL. Both protocols and implementations have known several critical vulnerabilities from Heartbleed to Poodle. The good news is: SSLv3 is finally dead, it’s time to move to something else.

I’ve recently added https support to my blog, and I thought it would be a good idea to share my SSL Labs A+ (with a SHA256 key) Poodle proof, Beast proof, Heartbeat proof configuration for Nginx. It was implemented on FreeBSD, which means you’ll have to change a few things here and there if you’re running on Linux, but most things are exactly the same.

Remember our pon.ey domain we recently added DNSSEC to? We’re now going to give him some https love.

Generate a strong SSL private key

First, you need to generate a strong sha256 private key for your SSL certificate. We won’t use the -des3 option to protect it with a password (you would need to type it every time you start Nginx, like after a random reboot), but we’ll use -rand/var/log/messages for some more randomness.

Don’t waste CPU cycles generating a 8196 bytes key, most SSL certificate resellers won’t accept it.

  # cd /usr/local/ssl
  # openssl genrsa -rand/var/log/messages 4096 -out pon.ey.key
  # chmod 400 pon.ey.key

Create a CSR with a SHA256 signature algorithm

You’re now going to generate the Certificate Signing Request you’ll send to your SSL reseller. Before chosing one, carefuly check he supports SHA256 CSRs.

SHA1 collision have occured since almost 10 years, and most vendors won’t accept SHA1 certificates anymore after 2016. If like me you’re chosing StartSSL you’ll have to renew your certificate when the implement SHA256.

# openssl req -new -nodes -sha256 -out pon.ey.csr  

Answer the few questions and send your CSR to your SSL reseller.

Nginx basic SSL configuration

Here’s the time to add some SSL love to your vhost. Here’s a basic Nginx vhost configuration. The first part is not SSL related but ensures your pon.ey lovers will use a secure connection.

server {
  listen  62.210.113.68:80;
  listen [::]:80;

  server_name  pon.ey;

  return 301 https://pon.ey$request_uri;
}

server {
  listen  62.210.113.68:443;
  listen [::]:443;

  server_name  pon.ey;

  ssl  on;
  ssl_certificate  /usr/local/etc/ssl/pon.ey.pem;
  ssl_certificate_key  /usr/local/etc/ssl/pon.ey.key;
  ssl_session_timeout  10m;
  ssl_prefer_server_ciphers on;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'AES256+EECDH:AES256+EDH';
  ssl_session_cache shared:SSL:10m;

  location / {
    root   /data/t37.net/public;

    access_log /data/t37.net/log/access.log;
    error_log 
  }
}

Note how we’re using a return 301 in the http only vhost instead of the classical rewrite rule relying on an often confusing regular expression (trick courtesy of Les Aker).

Let’s have a look at a few options there.

ssl_ciphers enables only AES256 with Ephemeral Diffie-Hellman and Ephemeral Elliptic-Curve Diffie-Hellman key exchange. It generates session keys so only the two parties involved in the communication can get them. No one else can, even if they can access the server’s private key. After the session is over and the session keys are destroyed, the only way to decrypt the communication is to break the session keys themselves. This protocol feature is called as forward secrecy.

ssl_protocols avoids broken SSLv1, SSLv2 and SSLv3 and enables TLS only. This means your site breaks with Internet Explorer 6, which may cause trouble in some corporate environment.

ssl_session_cache sets the type and size of caches that store session parameters. We’re using a shared cache named SSL and having a value of 10 megabytes. One megabyte can store about 4000 sessions, which should be enough for our pon.ey Web site.

ssl_session_timeout specifies the time during which the client is allowed to reuse the session parameters stored in cache.

Hardening EDH and EDCH

When using Ephemeral Diffie-Hellman ciphers, a prime number is shared between the client and the server to perform the key exchange. Nginx lets you specify the prime number you want the server send to the client, the bigger the better:

# openssl dhparam -out dh4096.pem -outform PEM -2 4096

Once you’re done (it can be long), add the following to your vhost:

ssl_dhparam /usr/local/etc/nginx/ssl/dh4096.pem;

HTTP Strict Transport Security

Next thing is to enable HTTP Strict Transport Security. This makes Nginx declare to users that he’ll use only HTTPS secured connections.

The HSTS policy is communicated to the client by the server using a HTTP response header named Strict-Transport-Security. HSTS policy specifies a period of time during which the user agent needs to access the server in a secure-only way.

Edit your vhost file, and add the following line just under the SSL configuration:

add_header Strict-Transport-Security max-age=535680000;  

Be careful when you add a long max age period: this means you’ll have to renew your SSL certificate if you want returning visitors to access your site during that period.

Configure SSL stapling

The Online Certificate Status Protocol (OCSP) is a protocol to check if a SSL certificate has been revoked. It’s been created to reduce the SSL negotiation time as an alternative to the Certificate Revocation List (CRL).

With CRL, the client downloads a list of revoked certificate and checks which can be huge and take lots of time to process. With OCSP, the client sends a request to a URL that returns the validity information of the certificate.

OCSP stapling is an alternative to OCSP that delegates the check to the certificate user instead of the Certification Authority.

Download the root CA and intermediate CA’s certificate of your SSL certificate in PEM format and save them in the same file. Save it as /usr/local/etc/pon.ey.trusted.pem

Add the following to your vhost configuration, following your SSL section.

ssl_trusted_certificate /usr/local/etc/ssl/pon.ey.trusted.pem;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;

Here, you’ll use Google DNS resolvers to query your certification authority for validity information.

Conclusion

Here you are. Your perfect Nginx SSL configuration is almost over. Before I let you go, here’s the complete vhost configuration as it should be:

server {
  listen  62.210.113.68:80;
  listen [::]:80;

  server_name  pon.ey;

  return 301 https://domain.com$request_uri;
}

server {
  listen  62.210.113.68:443;
  listen [::]:443;

  server_name  pon.ey;

  ssl  on;
  ssl_certificate  /usr/local/etc/ssl/pon.ey.pem;
  ssl_certificate_key  /usr/local/etc/ssl/pon.ey.key;
  ssl_dhparam /usr/local/etc/ssl/dh4096.pem;
  ssl_session_timeout  10m;
  ssl_prefer_server_ciphers on;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers 'AES256+EECDH:AES256+EDH';
  ssl_session_cache shared:SSL:10m;
  ssl_trusted_certificate /usr/local/etc/ssl/pon.ey.trusted.pem;
  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 8.8.4.4 8.8.8.8 valid=300s;
  resolver_timeout 10s;
  add_header Strict-Transport-Security max-age=535680000;
  
  location / {
    root   /data/t37.net/public;

    access_log /data/t37.net/log/access.log;
    error_log 
  }
}

If you have implemented DNSSEC, you can add your certificate fingerprint to your zone using a TXT field:

openssl x509 -in pon.ey.pem -outform DER | sha256 | awk '{print $1}'

Don’t forget to resign your zone after doing this!

Perry the Platypus wants you to subscribe now! Even if you don't visit my site on a regular basis, you can get the latest posts delivered to you for free via Email: