Guide to Nginx + SSL + SPDY
- Intro
- NGINX + SSL - Basics
- Multiple HTTPS - Servers/Certs
- intermediate-certs (thawte etc) using an SSL certificate chain)
- Choosing the right cipher-suites / Perfect Forward Security (PFS)
- SSL-analysis and optimization
- HSTS - Header
- SPDY
- SSL attacks and mitigations
- SSL Client-Authentication with Nginx
- complete and commented config-example
- Appendix
- Credits
- Changelog
- License
- Original Link:
Intro
a short documentation on how to setup and run nginx as SSL-Gateway/Offload, including SPDY. Beside basic configuration this guide covers HSTS-Headers, Perfect Forward Secrecy (PFS) and the latest and greatest ssl-based attacks like Heartbleed, BREACH, CRIME, BEAST and Lucky Thirteen. This guide shows how to configure various ssl-related options and performance-tuning, but doesnt explain much about the why; more details and references for each suggestion are available in each section.
the suggestions regarding cipher-suites and mitigation are found on different sites and cross-checked against various sources, especially tested via ssllabs.com to produce an A+ - ranking. if you rely on strong security you should let security-experts evaluate each suggestion. Additionally, we advise you to conduct your own performance/ssl-tests, expecially when trying different cipher-suites.
Notes and suggestions are valid as of Apr 2014.
disclaimer: the information presented herein is without any guarantees and we’ll take no responsibility if any harm happens to you or your users. If you find any factual problems, please contact us immediately and we will fix it ASAP.
When to use HTTPS/SSL
If you already have some parts of your website running in SSL, like login- or signup-forms, you should consider to switch to HTTPS for all parts of your site, if you haven't already; this blogpost explains why.
Whenever you use login/authentication you should enable SSL site-wide to protect your users from MITM-attacks and session-takeovers.
If you want to ensure the authenticity and/or integrity of your website from your users point-of-view you should enable SSL.
How to use HTTPS/SSL
For each virtualhost:443 you want to run under HTTPS you should create a corresponding server {} - directive that listens on HTTP and redirects all traffic to https://.... this is expecially usefull if you:
- change an existing site from HTTP to HTTPS to make user-bookmarks still work after the change
- want to workaround the case a user types in www.example.com w/out https:// infront; those users get redirected to the encrypted page
- have a buggy application that generates hardcoded/absolute uris or redirects.
If you use SSL on some part of your website already, you should enable SSL for all parts of your website. Really.
How to generate / obtain an SSL-certificate
For security it doesnt matter if you use a self-signed certificate or a certificate from a trusted authority. StartSSL.com offers free domain-validated certs; for extended-validated (EV) or wildcard - certs you might ask your hosting-provider.
You'll find a whole lot of guides on how to create a CSR (Certificate Signing Request) that is needed to obtain a cert from a trusted CA or how to generate a self-signed certificate; see this guide on openssl.com, this guide from akadia.com or this complete guide from didierstevens.com
When generating your CSR be sure to have 2048 bit key size; it is required by certificates that will expire after October 2013 src: verisign
NGINX + SSL - Basics
- configuring https servers @ nginx.org
- Setting up SSL on nginx for performance and security - step-by-step-guide
- Hardening Your Web Server’s SSL Ciphers
- 5 easy tips to accelerate SSL
- short nginx+ssl - howto @ code-bear.com
- detailed nginx+ssl-config-explanation
- HTTPSEverywhere at hasgeek.com
- SSL Server-Test @ ssllabs.com
- SSL/TLS Deployment Best Practices @ ssllabs.com
- SSL Server Rating Guide @ ssllabs.com
- Client-Side Certificate Authentication with nginx
- Module: Nginx OpenSSL version check
- How to Deploy HTTPS Correctly by eff.org
Since Nginx version 0.7.14 the preferred way of enabling SSL is by using
the ssl
parameter of the listen
directive.
There is a 3rd-party-module (nginx-openssl-version) available to check for the openssl-version used; you might want to use it to assure a certain version for your self-compiled nginx. This module should work with static or dynamically linked openssl-versions, thus ensuring integrity over availabilty.
# # standalone HTTPS-server + http-redirect; recommended setup # server { listen 443 ssl; server_name secure.example.com; ssl_certificate /etc/ssl/secure.example.com.crt; ssl_certificate_key /etc/ssl/secure.example.com.key; ... } # http-redirects to https server { listen 80 default_server; server_name secure.example.com; return 301 https://$host$request_uri; } ############################################### # # combined http/https-server # recommended, if you arent targetable by BREACH/BEAST-attacks server { listen 80; listen 443 ssl; server_name secure.example.com; ssl on; ssl_certificate /etc/ssl/secure.example.com.crt; ssl_certificate_key /etc/ssl/secure.example.com.key; # force https-redirects if ($scheme = http) { return 301 https://$server_name$request_uri; } ... }
Multiple HTTPS - Servers/Certs
Works-everywhere - solution
- create one IP for each server
server { listen 192.168.1.1:443 ssl; server_name secure1.example.com; ssl_certificate secure1.example.com.crt; ... } server { listen 192.168.1.2:443 ssl; server_name secure2.example.com; ssl_certificate secure2.example.com.crt; ... }
Using Wildcard/multiple certificates with several names
When using a configuration like this it's more efficient memory wise to place the certificate file containing the certificate(s) for all domain names and the corresponding private key file directives in a http context, such that it's inherited by all active servers/virtual hosts:
ssl_certificate secure.example.com.crt; ssl_certificate_key secure.example.com.key; server { listen 80; server_name www.example.com; ... } server { listen 443 default_server ssl; server_name secure.example.com; ... } server { listen 80; listen 443; server_name images.example.com; ... }
SNI - ServerNameIndication
- SNI @ nginx.org
- SNI-Client-Test @ velox.ch
- one IP for all SSL - servers
-
nginx / openssl MUST provide that feature; check with
nginx -V
must give a line like
nginx version: nginx/1.4.2 ... TLS SNI support enabled ... -
SNI - support is available in nginx 0.5.32 and above, but depends on / must be supported by your openssl-implementation
supporting browsers
- Opera 8.0;
- MSIE >= 7.0 (but only on Windows Vista or higher);
- Firefox >= 2.0 and other browsers using Mozilla Platform rv:1.8.1;
- Safari >= 3.2.1 (Windows version supports SNI on Vista or higher);
- Chrome >= v2012 (Windows version supports SNI on Vista or higher, too).
doesnt work
- Windows XP doesnt support SNI generally
- IE < 7
- Android version 2.x
intermediate-certs (thawte etc) using an SSL certificate chain)
- ssl-chains docu @ nginx.org
- concat server_cert(s) & intermediate_cert (exactly in that order) into a combined cert
server { ... ssl_certificate www.example.com.crt-combined; ... }
- verify the SSL certificate chain:
$ openssl s_client -connect www.example.com:443
Choosing the right cipher-suites / Perfect Forward Security (PFS)
- SSLLabs:Deploying Forward Secrecy
- SSLLabs: Configuring Apache, Nginx, and OpenSSL for Forward Secrecy
- ssl-ciper_suite - suggestiong from nginx-ml
- short howto @ code-bear.com
- detailed nginx-config-explanation
- HTTPSEverywhere at hasgeek.com
- Hardening Your Web Server’s SSL Ciphers
- 5 easy tips to accelerate SSL
- Security/Server Side TLS by Mozilla
Perfect Forward Secrecy
(Perfect) Forward Secrecy ensures the integrity of a session key in the event that a long-term key is compromised. PFS accomplishes this by enforcing the derivation of a new key for each and every session.
SSLLabs has a very detailed article on PFS: Deploying Forward Secrecy and also published a guide on Configuring Apache, Nginx, and OpenSSL for Forward Secrecy
Another interesting read is SSL/TLS & Perfect Forward Secrecy from Vincent Bernat.
While testing different cipher-suites we only found the ssllabs-suggestion fully enabling PFS when checking with ssllabs.com/ssltest.
Hasgeek.com - suggestions creating the best Rank @ ssllabs [Rank A, 100/90/100/90]
doesnt work
- IE <= 9
server { ... ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; # nginx.org - defaults; works always, limited PFS ssl_ciphers HIGH:!aNULL:!MD5; # suggestion from sslabs / including PFS, good 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; # additional cipher_suite - suggestions # from nginx-ml - no PFS #ssl_ciphers HIGH:!SSLv2:!MEDIUM:!LOW:!EXP:!RC4:!DSS:!aNULL:@STRENGTH; # nginx-default-for-static - no PFS #ssl_ciphers RC4:HIGH:!aNULL:!MD5; # suggestion from hasgeek.com #ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:HIGH:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!CBC:!EDH:!kEDH:!PSK:!SRP:!kECDH; # bare minimum for BEAST-mitigation + PFS #ssl_ciphers !aNULL:!LOW:!MD5:!EXP:RC4:AES256:3DES:AES128:SEED:CAMELLIA; # PFS and most secure ciphers (think about using TLS1.2 only) # suggestion from code-bear.com #ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:RC4-SHA; # PFS + BEAST-mitigation # suggestion from hynek.me #ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:RC4-SHA; # fast and secure, BEAST-mitigation but no PFS? # suggestion from http://unhandledexpression.com #ssl_ciphers ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM; # # suggestion my mozilla-server-team - good compatibility, pfs # #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK; ... }
SSL-analysis and optimization
optimizing for better SSL-performance
- ssl-server optimization @ nginx.org
- 5 easy tips to accelerate SSL
- SSL-Example - config @ nginx.org
-
ssl-cache & keepalive reduces load by reducing the number of ssl-handshakes
- type builtin: (dontuse) used by one worker process only. The cache size is specified in sessions. If size is not given, it is equal to 20480 sessions. Use of the built-in cache can cause memory fragmentation.
- type shared: (use exclusively) a cache shared between all worker processes. The cache size is specified in bytes; one megabyte can store about 4000 sessions. Each shared cache should have an arbitrary name. A cache with the same name can be used in several virtual servers.
- Both cache types can be used simultaneously, but using only shared cache without the built-in cache should be more efficient.
- 1 MB ssl - cache ~> 4000 Sessions
- gzip / expires can be used to speed up page-load and reduce traffic(http-compression != ssl-compression)
- possibly increase the session lifetime (by default, 5 minutes)
-
when serving static-content like images, css or javascript, e.g. content with no sensitive information, it should/could be enough to have a fast ssl_cipher implemented like RC4
-
the blogpost 5 easy tips to accelerate SSL suggests the cipher-suites below; while it has BEAST-protection it doesnt offer PFS, and we havent perf-tested this setup
http { ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; server_tokens off; server { ssl on; ... gzip on; expires 1d; ... keepalive_timeout 70; ... # nginx-default-for-static - no PFS #ssl_ciphers RC4:HIGH:!aNULL:!MD5; ... } }
testing SSL-setups
-
SSL Server-Test @ ssllabs.com for public-accessible Webservers
-
- testssl.sh is a free Unix command line tool which checks a server's service on any port for the support of TLS/SSL ciphers, protocols as well as some cryptographic flaws.
- repository: testssl.sh @ bitbucket
- 8ack-ssltools-collection (merely toosl to test ssh-setup from cli, but also vuln-checks and exploits found here and there)
$ testssl.sh smtp.gmail.com
- cipherscan - a simple way to find out which SSL ciphersuites are supported by a target.
$ ./cipherscan www.mare-system.de:443 prio ciphersuite protocols pfs_keysize 1 ECDHE-RSA-AES128-GCM-SHA256 TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits 2 ECDHE-RSA-AES256-GCM-SHA384 TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits 3 DHE-RSA-AES128-GCM-SHA256 TLSv1,TLSv1.1,TLSv1.2 4 DHE-RSA-AES256-GCM-SHA384 TLSv1,TLSv1.1,TLSv1.2 5 ECDHE-RSA-AES128-SHA256 TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits 6 ECDHE-RSA-AES128-SHA TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits 7 ECDHE-RSA-AES256-SHA384 TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits 8 ECDHE-RSA-AES256-SHA TLSv1,TLSv1.1,TLSv1.2 ECDH,P-256,256bits ...
- testing cipher_suites manually (please note: local test-results might depend on the locally installed openssl-version, so make sure to have the latest version available) :
# testing ssl_config $ openssl s_client -host www.mare-system.de -port 443 # testing for slow ciphers $ openssl s_client -host HOSTNAME -port 443 | grep DHE
HSTS - Header
HTTP Strict Transport Security (HSTS) is a web security policy mechanism whereby a web server declares that complying user agents (such as a web browser) are to interact with it using only secure HTTPS connections HSTS is an IETF standards track protocol and is specified in RFC 6797.
The HSTS Policy is communicated by the server to the user agent via a HTTP response header field named "Strict-Transport-Security". HSTS Policy specifies a period of time during which the user agent shall access the server in only secure fashion.
server { ... add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; ... }
SPDY
CAUTION: SPDY-Issues with the stable branch 1.4.x:
- SPDY might have some issues when used with proxy_cache (see here and here, this is valid up to at least nginx v 1.5.13 (apr 2014); check the 2nd link, if the ticket is still open or closed in future versions
- SPDY - bug: 499 response code on long running requests, see here
SPDY (pronounced speedy) is an open networking protocol developed primarily at Google for transporting web content. The goal of SPDY is to reduce web page load time. This is achieved by prioritizing and multiplexing the transfer of web page subresources so that only one connection per client is required. TLS encryption is nearly ubiquitous in SPDY implementations, and transmission headers are gzip-or DEFLATE-compressed by design. src: wikipedia
You can use selfsigned or CA-signed ssl-certificates with SPDY.
nginx supports SPDY - protocol in an experimental version since 1.3.15 and is available in stable-branch since 1.4.0. because it's not marked stable yet this features has to be switched on during compilation:
./configure ... --with-http_spdy_module --with-http_ssl_module ...
Requirements
SPDY can only be served via https and depends on spdy-aware browsers (like curent firefox and chrome). if spdy-servers detect a non-spdy-browser the connection is switched to a standard HTTPS-session to prevent compatibilty-issues.
- ssl enabled
- openssl v >= 1.0.1 (check with
openssl version
); e.g. spdy works with debian wheezy, but not with squeeze - regarding the add_header (see config below) there is a discussion wheter it is needed or not; see comments on this blogpost; we found nginx delivering pages via spdy without this additional header, but will leave here for completeness
Workaround for outdated OpenSSL-versions
- there is an option to have nginx run with spdy if you are forced to use openssl < 1.0.1:
- download and extract sources from openssl
- configure nginx to use your downloaded openssl-sources:
./configure ... --with-http_spdy_module --with-http_ssl_module \ --with-openssl=/path/to/openssl_source/ ...
check if spdy is enabled
-
to check your server:
- in chrome open
chrome://net-internals/#spdy
to see all spdy-sessions/servers your browser is connected to - SPDY - server-check @ spdycheck.org
- in chrome open
-
to check if your browser supports SPDY:
- isspdyenabled.com
- ist-spdy-aktiviert.de, german, but nicer interface
config
server { listen 443 ssl spdy; #add_header Alternate-Protocol 443:npn-spdy/2; }
SSL attacks and mitigations
Heartbleed
- OpenSSL Security Advisory 07 Apr 2014 - TLS heartbeat read overrun (CVE-2014-0160)
- The Heartbleed Bug
- NGINX and the Heartbleed vulnerability @ nginx.com
- existential type crisis : Diagnosis of the OpenSSL Heartbleed Bug
- IT Security FAQ @ heartbleed
- SSLLabs SSL-Test incl Heartbeat-Check
- filippo.io/Heartbleed/
- Heartbleed - Test @ mare-system.de
- serverfault.com / Heartbleed: What is it and what are options to mitigate it?
- Reddit netsec: "Diagnosis of the OpenSSL Heartbleed Bug"
- Reddit programming: The Heartbleed Bug
- Hacker-News: The Heartbleed Bug
- Module: Nginx OpenSSL version check
A Bug was published 2014-04-07 with a critical vuln in OpenSSL, allowing remote attackers to read up to 64k of memory of affected systems, including a possible compromise of ssl-server-keys. We were able to extract user-logins and sessioninformations from a major webmail-provider; so this bug must be considered critical, even if you have PFS implemented
affected versions:
- OpenSSL 1.0.1 through 1.0.1f (inclusive) are vulnerable
- OpenSSL 1.0.1g is NOT vulnerable
- OpenSSL 1.0.0 branch is NOT vulnerable
- OpenSSL 0.9.8 branch is NOT vulnerable
If you run nginx from your distro, you should update asap; updated versions are available for all major distributions as of 2014-04-08 (please dont forget to restart services; if possible, the whole server).
If you run an nginx-version with static-compiled openssl
you should rebuild and deploy your updated binaries. If you cannot
recompile with an updated openssl-version, you can rebuild
openssl with the -DOPENSSL_NO_HEARTBEATS
- flag to disable the
affected extension.
There is no mitigation or workaround; if your version is affected you need to update.
The following steps are suggested to recover from that bug:
- Patch vulnerable systems and reboot
- Regenerate new private keys.
- Submit new CSR to your CA.
- Obtain and install new signed certificate.
- Revoke old certificates.
Tests
local checks (please note: YMMV on all suggested commands given here):
- Check the compile-options from your local nginx with:
[ me@host :~] > nginx -V 2>&1 | grep "\-\-with-openssl"
- if the output is empty, openssl is (most probably) dynamically linked from your distro's version and already updated recently
- if the output looks similar the following you have openssl statically compiled into your nginx-binary. if you dont have the sources at hand, it's hard to tell which version was used, if not apparently displayed in the "--with-openssl" - path
configure arguments: --conf-path=/etc/nginx/nginx.conf --sbin-path=/usr/sbin/nginx ... --with-http_ssl_module ... --with-openssl=/path/to/openssl ...
- check your local openssl-version
[ me@host :~] > openssl version OpenSSL 1.0.1g 7 Apr 2014
- Phil Pennock announced an Nginx-Module to
write the openssl-version used to the logs and optionally set a minimum-version
with the added config-option like
openssl_version_minimum 1.0.1g;
; sources here this module should work with static or dynamic linked openssl (we did test only the static version)
... nginx: [emerg] OpenSSL runtime too old; asked for 1.0.2g, got: OpenSSL 1.0.1g 7 Apr 2014 ...
Checks against remote Servers:
BREACH
BREACH is an attack against SSL-encrypted sites that works if you have an HTTP response body that meets all of the following requirements:
- HTTP-compression (gzip, deflate)
- Your page reflects user data via query string parameters, POST - parameters
- Applications that serve PII/Tokens/sensitive data
Server-side mitigation is easy (see this discussion @ nginx - mailinglist) but would probably impact page_load_time:
- if running https only, disable gzip
- if running combined http/https - setup
- disable gzip globally or
- split your config into one https and one https part, enable gzip for your http-server and disable for your https-server
The BREACH-Team lists it's own suggestions for mitigation-strategies, but these would require changes within the webapp:
- Disabling HTTP compression
- Separating secrets from user input
- Randomizing secrets per request
- Masking secrets (effectively randomizing by XORing with a random secret per request)
- Protecting vulnerable pages with CSRF
- Length hiding (by adding random number of bytes to the responses)
- Rate-limiting the requests
Another Writeup on BREACH might be found here @ detectify.
BEAST / Lucky13
RC4 was considered a good solution for BEAST-mitigation, but since Lucky13 was developed TLS1.0 should be avoided, if possible. according to this post from Mr. SSL, Ivan Ristic, RC4 is considered not secure anymore. There's just one mitigation for this: use TLS1.2. The problem: TLS 1.2 is not yet widely supported by browsers. Although RC4-breakage is considered academic this statement could (and probabyl will) be invalid within the next years, so upgrading clients and servers to TLS 1.2 should happen as soon as possible.
"The difficulty is that, for public web sites that need to support a wide user base, there is practically nothing 100% secure they can use to replace RC4. We now have no choice but to accept that, no matter what settings we use, some segment of the user base will be at risk." RC4 in TLS is Broken: Now What? by Ivan Ristic
The attacks can only be carried out by a determined attacker who can generate sufficient sessions for the attacks. They recover a limited amount of plaintext. In this sense, the attacks do not pose a significant danger to ordinary users of TLS or WPA/TKIP in their current form. However, it is a truism that attacks only get better with time, and we anticipate significant further improvements to our attacks. On the Security of RC4 in TLS and WPA by Nadhem AlFardan, Dan Bernstein, Kenny Paterson, Bertram Poettering, Jacob Schuldt
At the moment (as of 2013) the common solution against BEAST is considered RC4, but update to TLS1.2 and using different cipher-suites should be implemented as soon as possible. If you have to support a wide range of OS and browsers, Lucky(13) You :)
RSA gives a short intro into the Lucky13 - problem in a blogpost: Secure Crypto: "Lucky Thirteen" Attack
server { ... ssl_prefer_server_ciphers on; # for more valid cipher-suites see [ complete and commented sample ] below # bare minimum fore BEAST - mitigation ssl_ciphers RC4:HIGH:!aNULL:!MD5; ... }
CRIME
- intro to CRIME - attack @ wikipedia
- nginx-default: ssl-compression OFF (since v 1.2.2 03 Jul 2012), see this answer from igor sysoev
- if you use an older version (you shouldnt): upgrade your nginx/openssl; if you cannot, maybe you can use this answer to turn off ssl-encryption (solution is untested)
SSL Client-Authentication with Nginx
- nategood.com: Client Side Certificate Auth in Nginx
- ssl_client_certificate @ nginx.org
-
make your CA (if you dont have already)
- generate Client-Certs
- install Client-Keys in Client devices
- install CACert root certs in server and client device.
- configure nginx
# credits: Nathan Good / https://github.com/nategood/sleep-tight # # Create the CA Key and Certificate for signing Client Certs openssl genrsa -des3 -out ca.key 4096 openssl req -new -x509 -days 365 -key ca.key -out ca.crt # Create the Server Key, CSR, and Certificate openssl genrsa -des3 -out server.key 1024 openssl req -new -key server.key -out server.csr # We're self signing our own server cert here. This is a no-no in production. openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt # Create the Client Key and CSR openssl genrsa -des3 -out client.key 1024 openssl req -new -key client.key -out client.csr # Sign the client certificate with our CA cert. Unlike signing our own server cert, this is what we want to do. openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
- nginx-config
server { listen 443; ssl on; ... ssl_client_certificate /etc/nginx/certs/ca.crt; ssl_verify_client optional; location /api/ { ssl_verify_client on; ... } location /fastcgi/ { root /var/www/example.com/html; fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME /var/www/example.com/lib/Request.class.php; fastcgi_param VERIFIED $ssl_client_verify; fastcgi_param DN $ssl_client_s_dn; ... } }
- accessing an api with client-certs:
curl -v -s -k --key client.key --cert client.crt https://secure.example.c/api/request/
complete and commented config-example
#openssl_version_minimum 1.0.1g; http { ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; server_tokens off; keepalive_timeout 60; # http-redirects to https; even if using of hsts; # usefull if users are typing your server-name w/out https:// server { listen 80 default_server; server_name secure.example.com; rewrite ^ https://secure.example.com$request_uri? permanent; # dummy redirect, if http+https-server-in-one if ($scheme = http) { return 301 https://$server_name$request_uri; } } server { # turn on ssl + spdy listen 443 ssl spdy default_server; server_name secure.example.com; # sending hsts-header / 12 months add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; # usually not needed #add_header Alternate-Protocol 443:npn-spdy/2; # ssl-config ssl_certificate /etc/ssl/secure.example.com.crt-combined; ssl_certificate_key /etc/ssl/secure.example.com.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # turn off gzip to protect against BREACH / http://breachattack.com/ gzip on; # # ssl cipher-suites # # nginx-default - kind of ok and working in usually any cases ssl_ciphers HIGH:!aNULL:!MD5 # nginx-default-for-static #ssl_ciphers RC4:HIGH:!aNULL:!MD5; # nginx mailinglist suggestion #ssl_ciphers HIGH:!SSLv2:!MEDIUM:!LOW:!EXP:!RC4:!DSS:!aNULL:@STRENGTH; # bare minimum for basic + BEAST-mitigation # grade A w/ ssllabs, but no PFS #ssl_ciphers RC4:HIGH:!aNULL:!MD5; # suggestion from sslabs #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; # suggestion from hasgeek.com #ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:HIGH:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!CBC:!EDH:!kEDH:!PSK:!SRP:!kECDH; # bare minimum for BEAST-mitigation + PFS #ssl_ciphers !aNULL:!LOW:!MD5:!EXP:RC4:AES256:3DES:AES128:SEED:CAMELLIA; # PFS and most secure ciphers (think about using TLS1.2 only) # suggestion from code-bear.com #ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:RC4-SHA; # PFS + BEAST-mitigation # suggestion from hynek.me #ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:RC4-SHA; # fast and secure, BEAST-mitigation but no PFS? # suggestion from http://unhandledexpression.com #ssl_ciphers ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM; # # suggestion my mozilla-server-team # #ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK; } }
Appendix
Perftesting Cipher-Suites
Server:
- 8 core / 32 GB ram, idleing at a load of max 0.61 while perftesting
Config:
- default:
worker_processes 8; events { worker_connections 3000; multi_accept on; } http { keepalive_timeout 65; keepalive_requests 100000; ... server { listen 80; listen 443 ssl; # ssl ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_prefer_server_ciphers on; ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; # various ciphers, see below # ssl_ciphers .... location /perftest/ { gzip off; expires off; access_log off; return 200; } } }
- ssl: various cipher_suites
Perf-Setup:
- ab w/wout keep-alive
- concurrency (-c) 10,20,30...500
- number of requests (-n) 100000 (keepalive) / 10000 no-keepalive
And the winner is ...
nginx default
ssl_ciphers HIGH:!aNULL:!MD5;
nginx mailinglist suggestion
ssl_ciphers HIGH:!SSLv2:!MEDIUM:!LOW:!EXP:!RC4:!DSS:!aNULL:@STRENGTH;
nginx-default-for-static
ssl_ciphers RC4:HIGH:!aNULL:!MD5;
SSLLabs.com - suggestion
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;
bare minimum for BEAST-mitigation
ssl_ciphers !aNULL:!LOW:!MD5:!EXP:RC4:AES256:3DES:AES128:SEED:CAMELLIA;
hasgeek.com - suggestion
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:HIGH:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!CBC:!EDH:!kEDH:!PSK:!SRP:!kECDH;
** unhandeledexception.com - suggestion
ssl_ciphers ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM
code-bearer.com - suggestion
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:RC4-SHA;
hynek.me - suggestion
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:RC4-SHA;
Credits
- wbr, Valentin V. Bartenev for correcting an issue with BREACH-Mitigation
- Nathan Good for SSL Client Authentication Examples
- Ivan Ristic for SSLLabs and SSL - related Blogposts
- all the writers of ml-entries amd blogposts, mentioned and linked throughout this document
- Maxim Dounin for his patience and nice helpfull comments on nginx-mailinglist
- Igor Sysoev & NGINX-Team for creating this fine software
Changelog
- 2014-04-17 - SPDY - warning / bug on long running requests
- 2014-04-10 - SPDY+proxy_cache issue-warning
- 2014-04-09 - heartbleed - suggestions by nginx.com, openssl-version - module, 8ack-ssltools-collection
- 2014-04-08 - heartbleed - bug
- 2014-04-04 - testssl.sh, cipherscan
- 2014-01-14 - minor changes in http -> https_redirect
- 2013-11-04 - modified ciphers and optimizing-hints for static-servers
- 2013-10-14 - perftesting cipher_suites - stub, more details on PFS
- 2013-10-10 - ssl-ciphers from nginx-ml, sni - clienttest - resource
- 2013-09-12 - License and Credits
- 2013-09-12 - SSL Client Authentication included
- 2013-09-12 - Wildcard-Certs with multiple hosts
- 2013-09-10 - BREACH-Attack - mitigations
- 2013-09-09 - updated and checked ssl_cipher-ratings, more resources from sslabs on PFS
- 2013-09-09 - initial version
License
This Guide is licensed as Creative Commons BY-SA and you are hereby granted the rights
- to share, copy, distribute and transmit the work
- to remix or adapt the work
- to make commercial use of the work
Under the following conditions:
- Attribution - You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
- Share Alike - If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one.