Updating NginX TLS settings for 2022

TLS NginX — Published on .

My blog (and pretty much every other site I host) is using Let’s Encrypt certificates in order to be served over https. Using any certificate at all is generally an upgrade when it comes to security, but your webserver’s configuration needs to be up to standard as well. Unlike the Let’s Encrypt certificates, keeping the configs up to date requires manual intervention, and is therefore something I don’t do often.

This week I decided I should check up on the state of my SSL configuration in nginx. I usually check this through SSL Lab’s SSL Server Test. This is basically testssl.sh in a shiny web frontend, and assigns you a score. I aim to always have A or higher, but it seemed my old settings were capped at a B. This was due to the cipher list allowing ciphers which are nowadays considered insecure.

While I could’ve just updated the cipher list, I decided to check up on all the other settings as well. There’s a GitHub gist which shows you what settings to use with nginx to make it secure by modern standards, which I’ve used to check if I should update some of my own settings.

My old settings looked as follows. Please don’t be too scared of the giant list in the ssl_ciphers.

# DHparams
ssl_dhparam /etc/nginx/dhparam.pem;

# SSL settings
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 off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_tickets off;
ssl_session_timeout 1440m;

# Additional headers
add_header Strict-Transport-Security "max-age=63072000" always;

With the new settings, I’ve added ssl_buffer_size and ssl_ecdh_curve. A friend on IRC pointed out I should enable ssl_prefer_server_ciphers, so this has been enabled too.

But most notably, the list of ssl_ciphers has been dramatically reduced. I still allow TLSv1.2 in order to allow slightly older clients to connect without any issues, but the ciphers considered WEAK have been disabled explicitly. This leaves a total of 5 ciphers to use, all of them using ECDHE, so the ssl_dhparam could be dropped as well.

Lastly, I’ve added a couple headers for security reasons, as recommended by securityheaders.com.

# SSL settings
ssl_protocols TLSv1.3 TLSv1.2;

ssl_buffer_size 4K;
ssl_ecdh_curve secp521r1:secp384r1;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:le_nginx_SSL:2m;
ssl_session_tickets off;
ssl_session_timeout 1440m;

ssl_ciphers 'EECDH+AESGCM:EECDH+AES256:!ECDHE-RSA-AES256-SHA384:!ECDHE-RSA-AES256-SHA';

# Additional headers
add_header Content-Security-Policy "default-src 'self'" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
note
I would still like the ssl_ciphers to be formatted in a more clean way, which I’ve tried to do with a variable through set, but it appears variables are not expanded within ssl_ciphers. If you have any methods to format the list of ciphers used in a cleaner way, I’d be very happy to know!

This configuration is saved as a small snippet which I include in all other site configurations, so it is updated everywhere at once as well. Now I should be able to neglect this for another year or two again.