Accessible yet secure NginX
Posted: 2018-08-07, 00:57
Just thought I'd share the tuned NginX configuration with you all here that is used on this (https-exclusive) forum.
A few non-standard things in place (because of my preference for Camellia over AES as a symmetric cypher) but that can always be changed if you want to prefer the use of Rijndael-based cyphers.
Running through the config and lifting out important bits:
The first server {} block makes sure to redirect any http requests to https (not logging these redirections). Any errors are still logged to catch oddities/abuse/attacks.
Straight-forward and shouldn't be difficult to understand.
Open port 443 (https) for TLS connections on both IPv4 and IPv6, and enable HTTP/2 on it.
Provide TLS 1.2 connections with fallback to v1.1 and v1.0 for accessibility from older clients
This is the preferred cypher order on this server, giving preference to Camellia with ephemeral Diffie-Hellman key exchange. Although this exchange is relatively expensive (It uses fixed parameters and not an elliptic curve), the lack of ECDHE for Camellia in common crypto libraries due to political decisions rather than technical ones enforces plain DHE for this. This is still safe because of a large key at the root of it (see dhparam). I'm a proponent of Camellia as a symmetric cypher because, unlike AES, there are no known weakening attacks against it and it's adopted as a strong cypher by several leading crypto authorities in the world. (Firefox users are out of luck because Mozilla has specifically disabled this cypher even if NSS supports it).
Beyond that, preferring Elliptic Curve and Galois-Counter modes for cypher suites, sorting them in most secure to least secure order gives a very secure end result (forward secrecy is used in all supported clients). Weak/broken cyphers like RC4, 3DES, MD5-based HMAC, Export and low sec suites are all disabled.
This is the pre-generated DHE parameter file using a 4096-bits RSA key. If you enable DHE-based cypher suites in your server, you should always generate these parameter files with sufficient size (at least 2048 bits) to prevent LOGJAM related weakness.
You can generate this with OpenSSL:
openssl dhparam -out RSA4096.pem -5 4096
NginX doesn't use an optimal elliptic curve by default (opting for a fast one instead). This line makes sure a curve algorithm with higher security is selected for all EC exchanges.
We make sure our carefully-selected order in ssl_ciphers is used with this line.
Of course we want to make sure sessions are re-used to lower load on the server/reduce delays for visitors from re-establishing TLS all over again.
This enables and uses OCSP stapling, one of the chief mechanisms for quick and secure verification of the TLS certificates in use without having to call out to OCSP servers from the browser (increases client privacy and performance). We're using Cloudflare, HE and Google DNS servers for resolving, which should provide plenty of redundancy for the required server->OCSP connections.
No need to broadcast what server and version we're using.
Enable HSTS for this domain with a sufficiently-long time-to-live.
This tunes miscellaneous headers to leverage client security features.
(If you want to know more about these headers, please inform yourself -- there are plenty of documents describing these features out on the web)
Result: And all supported clients (everything except IE/XP and old java versions, pretty much) will use forward secrecy.
Cypher strength is not maxed out on purpose, because we want to strike a balance here to keep maximum accessibility while not compromising security.
I hope this is of use to anyone out there
A few non-standard things in place (because of my preference for Camellia over AES as a symmetric cypher) but that can always be changed if you want to prefer the use of Rijndael-based cyphers.
Code: Select all
server {
listen 80;
listen [::]:80;
server_name mydomain.org;
access_log off;
error_log /path/to/error.log;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /etc/nginx/certs/mydomain.crt;
ssl_certificate_key /etc/nginx/certs/mydomain.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers EDH+CAMELLIA:EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:AES256:HIGH:AES128:CAMELLIA:!RC4:!3DES:!SEED:!aNULL:!LOW:!MD5:!EXP;
ssl_dhparam /etc/nginx/RSA4096.pem;
ssl_ecdh_curve secp384r1;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 4.2.2.1 8.8.4.4 valid=300s;
resolver_timeout 5s;
server_tokens off;
add_header Strict-Transport-Security "max-age=31536000;";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1";
add_header X-Frame-Options "SAMEORIGIN";
add_header Referrer-Policy "strict-origin-when-cross-origin";
server_name mydomain.org;
access_log /path/to/access-ssl.log;
error_log /path/to/error-ssl.log;
root /path/to/public_html;
client_max_body_size 25M;
[any specific location rules, e.g. for php, go here]
}
The first server {} block makes sure to redirect any http requests to https (not logging these redirections). Any errors are still logged to catch oddities/abuse/attacks.
Straight-forward and shouldn't be difficult to understand.
Code: Select all
listen 443 ssl http2;
listen [::]:443 ssl http2;
Code: Select all
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
Code: Select all
ssl_ciphers EDH+CAMELLIA:EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:AES256:HIGH:AES128:CAMELLIA:!RC4:!3DES:!SEED:!aNULL:!LOW:!MD5:!EXP;
Beyond that, preferring Elliptic Curve and Galois-Counter modes for cypher suites, sorting them in most secure to least secure order gives a very secure end result (forward secrecy is used in all supported clients). Weak/broken cyphers like RC4, 3DES, MD5-based HMAC, Export and low sec suites are all disabled.
Code: Select all
ssl_dhparam /etc/nginx/RSA4096.pem;
You can generate this with OpenSSL:
openssl dhparam -out RSA4096.pem -5 4096
Code: Select all
ssl_ecdh_curve secp384r1;
Code: Select all
ssl_prefer_server_ciphers on;
Code: Select all
ssl_session_cache shared:SSL:10m;
Code: Select all
# Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 4.2.2.1 8.8.4.4 valid=300s;
resolver_timeout 5s;
Code: Select all
server_tokens off;
Code: Select all
add_header Strict-Transport-Security "max-age=31536000;";
Code: Select all
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1";
add_header X-Frame-Options "SAMEORIGIN";
add_header Referrer-Policy "strict-origin-when-cross-origin";
(If you want to know more about these headers, please inform yourself -- there are plenty of documents describing these features out on the web)
Result: And all supported clients (everything except IE/XP and old java versions, pretty much) will use forward secrecy.
Cypher strength is not maxed out on purpose, because we want to strike a balance here to keep maximum accessibility while not compromising security.
I hope this is of use to anyone out there