Configure the HTTPS server on nginx

Why am I writing this?


Recently, due to a bunch of factors (NSA, DPI with advertising, and more), paranoia started to wake up and I thought to completely transfer my small site to https. There were several articles on the hub with technical details of SSL / TLS, however, looking for information on configuring the https web server, I found the traditional division of articles - either these are “Do it like this” articles, where settings are simply given without any explanation and use cases, or these are large theoretical articles discussing various patterns of use, but without practically applicable off-the-shelf options. On a habr there was an article about the setting, but there is no information about DH encodings, and some parameters are not described. I thought it’s worth ordering what was found as an article, which will be useful to those who would like to deploy https on their server, but not go too deep into the wilds of SSL.

The narration will be conducted taking into account the fact that nginx acts as the web server (and in one place there will be a parameter for php-fpm).

Certificate


I already had a certificate from StartSSL. About it already wrote on a habr, so I will not linger on this step. I can only say that during the first two or three days, browsers checking the certificate on the server can swear at it (this happened to me with Opera 12 and Firefox), apparently at StartCom the caches of valid certificates are not updated so often. About the installation, it will be said below

About customization options


Nginx out of the box in the new versions offers practically relevant, but still requiring polishing parameters, however, the actual parameters appeared in the standard config not so long ago, so in some cases the standard example of an HTTPS server in the config will not be relevant.

In general, there are two currently relevant configuration options - with and without Forward Secrecy. When setting up, the difference is only in the set of encodings (ssl_ciphers directive), but here it’s worth considering what you want from https.

You can read about Forward Secrecy here. . In a nutshell, the bottom line is that for the current RC4 algorithm, session keys are generated based on the server’s private key. Thus, if the private key is compromised, it will be possible to decrypt all sessions (if they were recorded). In the case of using DH encodings, each session has its own set of keys, which sessions do not depend on the private key. However, in this case much more processor time is spent on the handshake, which increases the load and the time it takes to open the page.

Here it is worth considering why https is needed specifically on your site. With a large number of visitors, the use of DH encryption algorithms can decently increase the load (which in any case will increase when switching to HTTPS), in some cases it will be necessary to increase the tariff for VDS, etc. In most cases, RC4 is enough, but many want it to be “top class”, so why not do it if the resources allow?

Nginx setup


As a result of the settings, I got about such a config, I will explain the essence of the parameters below.

In the http section, you must add:
ssl_session_cache   shared:SSL:10m;
ssl_session_timeout 5m;
ssl_prefer_server_ciphers on;
ssl_stapling on;
resolver 8.8.8.8;


The server section will turn out approximately like this:
server {
    listen       443 ssl;
    server_name  www.site.ru;
    .......

    keepalive_timeout   60;
    ssl_certificate      certificate_bundled.crt;
    ssl_certificate_key  privatekey.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers  "HIGH:!RC4:!aNULL:!MD5:!kEDH";
    add_header Strict-Transport-Security 'max-age=604800';

	.......
    location ~ \.php$ {
	.......
        fastcgi_param HTTPS on; # Для php-fpm
	.......
    }
}


In this example, no DH algorithms are used, i.e. No Forward Secrecy. Of the improvements here, we can omit SSLv3 support (removing it from ssl_ciphers), thus IE 6 and below will no longer be supported, since it does not support TLS, as well as increase the STS time, but more on that below.
Without SSLv3, this setting gives a score of 100-95-100-90 in the SSL test .

Let's go through the parameters


ssl_session_cache shared: SSL: 10m;
ssl_session_timeout 5m;

“Sets the type and size of caches for storing session parameters.” (nginx.org) A cache is necessary for the possibility of reusing session keys, so when establishing a new connection old keys will be used, i.e. Handshake will not be re-produced. It is especially important when using DHE encoding (for example, in Opera 12 browser), since the page loading time with all elements is greatly increased in the absence of a cache, and DHE also uses more resources and time (relative to EECDH and RC4). The shared parameter sets the general nginx cache for all workflows, 10m - cache size (10 MB, with 1 MB ~ 4000 sessions, so with these settings you can store up to 40 thousand sessions), 5m - session timeout in the cache (5 minutes) .

ssl_prefer_server_ciphers on;
“Specifies that server ciphers should be prioritized over client ciphers when using the SSLv3 and TLS protocols.” (nginx.org) - Client Ciphers (CBC) are vulnerable to certain types of attacks.

ssl_stapling on;
Allows the server to attach OCSP responses, thereby reducing page load time for users. Here we mean answers about the validity of the certificate (when checking for revocation). From the point of view of user security, it does not matter who sends the answers - a web server or a CA server - after all, the answer is signed in any case and the validity of the response can also be checked, and the answer includes its expiration date.
For this function to work, you need to specify the DNS server, which is done by the resolver directive.

keepalive_timeout - I think it doesn’t need a description, do not turn it off or set it too small to reduce the load due to re-establishing the connection.

ssl_certificate and ssl_certificate_key point to the certificate file and the private key file for it. Since I am telling about the example of a certificate from StartSSL, here I will make a small comment regarding the StartSSL instructions for installing the certificate - you do not need to add the Root CA certificate to the generalized certificate file, since this does not make sense and only increases, although not by much, the size of the transmitted data. It is enough to have a personal certificate and a certificate of an intermediate certification authority in the file. The finished certificate file for nginx (for the StartSSL certificate) can be obtained with the following command:
cat certificate.crt sub.class1.server.ca.pem > certificate_bundled.crt

Where your certificate is certificate.crt and the intermediate certificate is www.startssl.com/certs/sub.class1.server.ca.pem
add_header Strict-Transport-Security 'max-age = 604800';
Strict-Transport-Secutiry - a header that tells the browser that the site is accessible only via https. This prevents the possibility of switching back to the http version for a subsequent attack through an unencrypted connection. By the way, this parameter is also convenient in that if there is a “forgotten” resource connection in the page code (picture / script / style / ...) from the same site via http, the browser itself will go to the https version and will not swear at partially unencrypted compound. Of course, this will not work for external resources. Time is a week. Many recommend setting it to 1 year, but if you decide not to use https in the future, this can cause problems for some users. The time is updated with each transmission of this header, i.e. every time you visit the site.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
Indicates supported protocols. SSLv2 and v3 have critical vulnerabilities.

ssl_ciphers "HIGH:! RC4:! aNULL:! MD5:! kEDH";
Indicates the ciphers used. Actually due to changes in the cipher suite, Forward Secrecy is configured. It differs from the standard set offered by nginx only with the! KEDH parameter,

Forward secrecy


To enable Forward Secrecy, you can use, for example, the following cipher suite:
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";


In addition, you must configure the priority of OpenSSL ciphers:
openssl ciphers -V 'EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA256 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EDH+aRSA EECDH !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS'

This option does not prohibit the use of RC4 to maintain compatibility with some browsers, but not so long ago vulnerabilities were discovered in it , albeit practically difficult to implement.

To strengthen encryption, you can increase the strength of DH ciphers by creating a DH cipher parameters file (creating a file will take some time!), Say 4096 bits long:
openssl dhparam -out dh4096.pem 4096

And adding the directive to the nginx config
ssl_dhparam dh4096.pem;

This can be done for, say, the web-based server / service management interfaces, but the handshake will take even longer, so you should not do this on a regular site.

About CDN Services


In a discussion of setting up Forward secrecy instructions, it was noticed that at least Amazon CloudFront CDN does not support DH-encoded exchanges with your server, and RC4 seems to be a bit too bad. It is possible that with other CDNs, too, not everything is perfect, but I personally have not encountered them yet, so I can’t say anything.

useful links


Testing https-web server
settings Apache and nginx settings for Forward Secrecy