Getting an A+ on Qualy's SSL Labs Tester

Security, SSL Posted on

I recently spent a few hours trying to get a perfect score on Qualy's SSL Labs Tester. While I was not able to achieve a "100" in every category, I feel I got pretty close:

sethvargo.com SSL Score

This post will detail the steps for getting an A+ SSL rating using Nginx.

Generate a Certificate

The first step in securing your server with SSL is to generate an SSL certificate. At this time, the only way to get a verified certificate that will be trusted across most Internet browsers is to pay for a certificate. I chose RapidSSL, but you can choose any respectable provider.

You do not need to purchase an extended validation (EV) certificate to achieve an A+ rating. For personal blogs or sites that are not processing secure information, a regular certificate is fine. There is no additional encryption added with an EV certificate - just a pretty green bar that makes users feel better.

Depending on the provider you chose, you will need to generate a CSR and securely transmit the files onto your server. You may also need to install an intermediate certificate.

Install the Packages

In my case, I used Nginx and OpenSSL. The latest version of OpenSSL in Ubuntu's is good enough, but the latest Nginx does not support the SSL stapling we want to use later.

$ sudo apt-get install openssl

Because these instructions could easily become out of date, I recommend following the steps for compiling Nginx from source on the Nginx website.

Choose Protocols

This is arguably the hardest decision you will need to make. If you want to achieve an A+ rating, you will need to neglect a small percentage of your user base.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

This tells Nginx to explicitly only allow TLS, which means older clients (namely IE 6 and Windows XP users) will get certificate errors when visiting your website. If any of these are your target audience, you must also add SSLv3 to the list, but you will be unable to get an A+ by doing so (you can still get an A).

Choose Ciphers

Below, we only permit 256-bit encryption schemes. The ECDHE suite gives us Forward Secrecy (although we will generate a new set of dhparams in a later step). It is important to note that these values are in order of specificity, so the ordering is from best to worst.

ssl_prefer_server_ciphers on;
ssl_ciphers AES256+EECDH:AES256+EDH:!aNULL;

Again, if you plan to support IE6 or older clients, you may need to add additional cipher suites. Doing so will reduce your rating.

Generate new dhparams

With Forward Secrecy, if an attacker gets a hold of the server's private key, it will not be able to decrypt past communications. The private key is only used to sign the DH handshake, which does not reveal the pre-master key. Diffie-Hellman ensures that the pre-master keys never leave the client and the server, and cannot be intercepted by a MITM.

All versions of Nginx as of 1.4.4 rely on OpenSSL for input parameters to Diffie-Hellman (DH). Unfortunately, this means that Ephemeral Diffie-Hellman (DHE) will use OpenSSL's defaults, which include a 1024-bit key for the key-exchange. Since we're using a 2048-bit certificate, DHE clients will use a weaker key-exchange than non-ephemeral DH clients.

We need generate a stronger DHE parameter:

$ cd /etc/ssl/certs
$ openssl dhparam -out dhparam.pem 4096

And then tell Nginx to use it for DHE key-exchange:

ssl_dhparam /etc/ssl/certs/dhparam.pem;

Please note: this section was adopted from Strong SSL Security on nginx at Raymii.org.

Turn on SSL

In your Nginx configuration, you will need to activate SSL. The code below is documented inline to describe each option.

# Enable SSL on all domains - you may also want to enable this on a per-site
# basis instead if you are supporting multiple virtual hosts.
ssl on;

# Cache SSL sessions for 10m (this is about 40,000 sessions), timing them out
# after 24 hours.
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;

# Set the buffer size to 1400 bytes (that way it fits into a single MTU).
ssl_buffer_size 1400;

OCSP Stapling

When connecting to a server, clients should verify the validity of the server certificate using either a Certificate Revocation List (CRL), or an Online Certificate Status Protocol (OCSP) record.

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;

HSTS

HTTP Strict Transport Security (HSTS) instructs browsers to communicate with your website only over SSL.

# Enable HSTS
add_header Strict-Transport-Security max-age=63072000;

# Do not allow this site to be displayed in iframes
add_header X-Frame-Options DENY;

# Do not permit Content-Type sniffing.
add_header X-Content-Type-Options nosniff;

Intermediate Certificates

Depending on when you purchase your certificate, you may be issued a certificate that uses SHA1 encryption. Many browsers, such as Chrome and Safari will soon distrust these certificates and show a warning.

In my case, with RapidSSL, I needed 256-bit GeoTrust and RapidSSL intermediate certificates. It took some searching, but I was able to find the SHA 256 intermediate certificates on each of the provider's websites. You may need to contact your SSL certificate provider to obtain the SHA 256 intermediate certificate. You must have a certificate using SHA 256 to obtain an A+ on the SSL labs tester.

GeoTrust SHA256 Intermediate Certificate

-----BEGIN CERTIFICATE-----
MIIERDCCAyygAwIBAgIDAjp4MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMTQwODI5MjIyNDU4WhcNMjIwNTIwMjIyNDU4WjBmMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UECxMURG9tYWluIFZh
bGlkYXRlZCBTU0wxIDAeBgNVBAMTF0dlb1RydXN0IERWIFNTTCBDQSAtIEc0MIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA30GUetr35DFDtuoBG1zOY+r6
baPZau4tmnX51ZxbvTTf2BzJbdgEiNputbe18DCuQNZd+sRTwdQinQROEaaV1UV8
QQVY4Ezd+e5VvV9G3K0TCJ0s5PeC5gcrng6MNKHOxKHggXCGAAY/Lep8myiuGyiL
OQnT5/BFpLG6EWeQVXuP3u04XKHh44PEw3KRT5juHMKAqmSlPoNiHMzgnvhawBMS
faKni6PnnyrXm8rL7ZcBnCiEUQRQQby0/HjpG88U6h8P/C4BMo22NcsKGDvsWj48
G9OZQx4v973zWxK5B17tPtGph8x3cifU2XWiY0uTNr3lXNe/X3kNszKnC7JjIwID
AQABo4IBHTCCARkwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4wHQYD
VR0OBBYEFAtQ7HfvKpv/7AOhCv+txuQqGMc+MBIGA1UdEwEB/wQIMAYBAf8CAQAw
DgYDVR0PAQH/BAQDAgEGMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9nLnN5bWNi
LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAuBggrBgEFBQcBAQQiMCAwHgYIKwYBBQUH
MAGGEmh0dHA6Ly9nLnN5bWNkLmNvbTBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYw
MzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2Vz
L2NwczANBgkqhkiG9w0BAQsFAAOCAQEAMyTVkKopDDW5L8PHQpPAxhBLAwh2hBCi
4OdTEifyCtp/Otz9XHlajxd0Q1Ox1dFdWbmmhGTK8ToKWZYQv6mBV4tch9x/4+S7
BXqgMgkTThCBKB+cA2K89AG1KYNGB7nnuF3I6dHdrTv4NNvB0ZWpkRjtPCw3EU3M
/lM+UEP5w1ZBrFObbAWymuLgWVcwMrYmThMlzfpIcA91VWAR9TvVXlo8i1sPD2JC
SGGFixD0wYi/f1+KwtfNK5RcHzRKCK/rromoSHVVlR27wJoBufQDIj7U5lIwDWe5
wJH9LUwwjr2MpQSRu6Srfw/Yb/BmAMmjXPWwj4PmnFrmtrnFvL7kAg==
-----END CERTIFICATE-----

RapidSSL SHA256 Intermediate Certificate

-----BEGIN CERTIFICATE-----
MIIEJTCCAw2gAwIBAgIDAjp3MA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMTQwODI5MjEzOTMyWhcNMjIwNTIwMjEzOTMyWjBHMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXUmFwaWRTU0wg
U0hBMjU2IENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv
VJvZWF0eLFbG1eh/9H0WA//Qi1rkjqfdVC7UBMBdmJyNkA+8EGVf2prWRHzAn7Xp
SowLBkMEu/SW4ib2YQGRZjEiwzQ0Xz8/kS9EX9zHFLYDn4ZLDqP/oIACg8PTH2lS
1p1kD8mD5xvEcKyU58Okaiy9uJ5p2L4KjxZjWmhxgHsw3hUEv8zTvz5IBVV6s9cQ
DAP8m/0Ip4yM26eO8R5j3LMBL3+vV8M8SKeDaCGnL+enP/C1DPz1hNFTvA5yT2AM
QriYrRmIV9cE7Ie/fodOoyH5U/02mEiN1vi7SPIpyGTRzFRIU4uvt2UevykzKdkp
YEj4/5G8V1jlNS67abZZAgMBAAGjggEdMIIBGTAfBgNVHSMEGDAWgBTAephojYn7
qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUw5zz/NNGCDS7zkZ/oHxb8+IIy1kwEgYD
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNQYDVR0fBC4wLDAqoCig
JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMC4GCCsGAQUF
BwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDovL2cuc3ltY2QuY29tMEwGA1UdIARF
MEMwQQYKYIZIAYb4RQEHNjAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3Ry
dXN0LmNvbS9yZXNvdXJjZXMvY3BzMA0GCSqGSIb3DQEBCwUAA4IBAQCjWB7GQzKs
rC+TeLfqrlRARy1+eI1Q9vhmrNZPc9ZE768LzFvB9E+aj0l+YK/CJ8cW8fuTgZCp
fO9vfm5FlBaEvexJ8cQO9K8EWYOHDyw7l8NaEpt7BDV7o5UzCHuTcSJCs6nZb0+B
kvwHtnm8hEqddwnxxYny8LScVKoSew26T++TGezvfU5ho452nFnPjJSxhJf3GrkH
uLLGTxN5279PURt/aQ1RKsHWFf83UTRlUfQevjhq7A6rvz17OQV79PP7GqHQyH5O
ZI3NjGFVkP46yl0lD/gdo0p0Vk8aVUBwdSWmMy66S6VdU5oNMOGNX2Esr8zvsJmh
gP8L8mJMcCaY
-----END CERTIFICATE-----

Final Configuration

If you are just looking for a copy-paste solution to get an A+, you can copy and paste the Nginx configuration below.

ssl on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 24h;
ssl_buffer_size 1400;
ssl_session_tickets off;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ssl_ciphers AES256+EECDH:AES256+EDH:!aNULL;
ssl_prefer_server_ciphers on;

ssl_certificate /etc/ssl/website.com.crt;
ssl_certificate_key /etc/ssl/website.com.key;
ssl_dhparam /etc/ssl/dhparam.pem;

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;

spdy_keepalive_timeout 300;
spdy_headers_comp 9;

add_header Strict-Transport-Security max-age=63072000;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

You will still need to generate and install the proper certificates. I hope this blog post helps you install and secure your website. Please feel free to leave a comment or suggestion!

About Seth

Seth Vargo is an engineer at Google. Previously he worked at HashiCorp, Chef Software, CustomInk, and some Pittsburgh-based startups. He is the author of Learning Chef and is passionate about reducing inequality in technology. When he is not writing, working on open source, teaching, or speaking at conferences, Seth advises non-profits.