Configuring your server for SSL can be a little overwhelming. To help with this I am writing three posts (one for Nginx, Apache and IIS) with example configurations that (to the extent possible) result in the same configuration regardless of what server you are using.
Let’s start with Nginx, for this site :
- Running nginx/1.4.1 and openssl 1.0.1e
- All static content is handled by Nginx.
- All dynamic content is handled by Node.js and Express.
- We use the X-Frame-Options header to help protect from Click-Jacking.
- We use the X-Content-Security-Policy
header to help protect from Cross-Site-Scripting. - All requests for content received over http are redirected to https.
- Once the user visits the https version of the site the Strict-Transport-Security header instructs the browser to always start with the https site.
- We have chosen SSL cipher suites to offer a blend of performance and security.
- We have disabled SSL v2 and v3 and enabled all versions of TLS.
- We have enabled OCSP stapling.
- We have enabled SSL session caching.
- We have put all certificates and keys into their own folder (certs.d/).
- Set the owner of the of the certs.d folder to the process that the server runs as.
- We have restricted the certs.d folder and key files so only the owner can read and write (chmod 600).
Here is the configuration file:
server { listen 80; server_name example.com; # tell users to go to SSL version this time if ($ssl_protocol = "") { rewrite ^ https://$server_name$request_uri? permanent; } } server { listen 443 ssl; server_name example.com; # tell users to go to SSL version next time add_header Strict-Transport-Security "max-age=15768000; includeSubdomains;"; # tell the browser dont allow hosting in a frame add_header X-Frame-Options DENY; # tell the browser we can only talk to self and google analytics. add_header X-Content-Security-Policy "default-src 'self'; \ script-src 'self' https://ssl.google-analytics.com; \ img-src 'self' https://ssl.google-analytics.com"; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # ciphers chosen and ordered for mix of performance, interoperability and security #ssl_ciphers AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH; # ciphers chosen for security (drop RC4:HIGH if you are not worried about BEAST). #ssl_ciphers RC4:HIGH:HIGH:!aNULL:!MD5; # ciphers chosen for FIPS compliance. #ssl_ciphers !aNULL:!eNULL:FIPS@STRENGTH; # ciphers chosen for forward secrecy an compatibility ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"; ssl_prefer_server_ciphers on; ssl_certificate_key certs.d/example.key; ssl_certificate certs.d/example.cer; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # enable ocsp stapling resolver 8.8.8.8; ssl_stapling on; ssl_trusted_certificate certs.d/example.cer; # let nginx handle the static resources location ~ ^/(htm/|html/|images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) { root /usr/share/nginx/example/public; access_log off; expires @30m; } # redirect to node for the dynamic stuff location / { proxy_pass http://localhost:8003; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_hide_header X-Powered-By; #proxy_redirect off; #proxy_set_header X-Real-IP $remote_addr; #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #proxy_set_header X-Forwarded-Proto $scheme; #proxy_set_header X-NginX-Proxy true; } error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
Thank you for the great post.
I’ve used your example configuration on one of my websites, I’m particularly looking at ocsp stapling support.
Is there any way to confirm ocsp is working correct? I’ve tested with https://www.ssllabs.com/ssltest/ but under protocol details it shows OCSP stapling not supported, albeit I have it turned on in my nginx configuration.
My error log is logging the following when visiting the website:
[error] 15594#0: OCSP_basic_verify() failed (SSL: error:27069065:OCSP routines:OCSP_basic_verify:certificate verify error:Verify error:unable to get issuer certificate) while requesting certificate status, responder: ocsp.thawte.com
Here is a post I did on testing OCSP Stapling – http://unmitigatedrisk.com/?p=100
This error indicates that you probably do not have all of the right certificates in the ssl_trusted_certificate file.
Check out the sslcheck.globalsign.com configuration checker, it will suggest the right certificates to include in that file.
Please try to avoid using if commands for the redirect. Nginx wiki has a nice tutorial here: http://wiki.nginx.org/IfIsEvil
Agreed If is evil but in this case not sure how I would restructure to accomplish the same thing.
How about this?
server {
listen 80;
server_name domain.com;
rewrite ^ https://$server_name$request_uri? permanent;
}
Thank you very much for this guide, which helped my site go from an F to an A+ with SSLLabs server test.