Edited at

Qualys SSL Server Test で A+評価&満点を取得する (nginx)

Qualys SSL Server Test: https://www.ssllabs.com/ssltest/

ssltest.png


追記


  • 2018-12-05 OpenSSL v1.1.1 および TLS v1.3 用の対応を追記。

  • 2019-04-04 TLS_AES_128_GCM_SHA256 無効化について追記。

  • 2019-08-31 CBC 暗号スイートの無効化について追記。


注意

A+ 評価はいいとしても、満点を取ろうとすると、対応ブラウザが少なくなったり、サーバ負荷が高くなったりします。

実運用では A もしくは A+ 評価を目指すまでに留め、満点は趣味でやってみるくらいでいいかと思います。


証明書取得時の注意

もし満点を達成しようとするのであれば、証明書の鍵長を 4096bit 以上にする必要があります。

鍵長が 4096bit 以上でなければ、 Key Exchange が 100 になりません。

参考: Let’s EncryptでSSL/TLS証明書のRSA鍵長を4096ビットにする方法 | Webセキュリティの小部屋


SSL 関係の設定 (アルファベット順)

# HSTS を有効にする。

# これを有効にすると、クライアントは、初回接続以降は「必ず」 https で接続してくるようになる。
# 有効にしないと A が評価の上限になり、有効にすることで A+ 評価になる。
# 「必ず」 https 接続になっていいかどうかはサービスによるので、設定には注意すること。
add_header Strict-Transport-Security 'max-age=31536000;' always;

# HSTS Preload に登録する場合は、サブドメインも全て https に対応させ、
# ヘッダに includeSubDomains と preload も含めるようにする。
# @see https://hstspreload.org/
#add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;

# OCSP Stapling に対応するため、ネームサーバを設定する必要がある。
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# 証明書に対応する公開鍵を指定。
# 自分自身の証明書~中間証明書~ルート証明書の一つ前 までの順に結合したファイルを指定する。
# COMODO の PositiveSSL ならば、送付される証明書に含まれる
# {your domain}.crt
# COMODORSADomainValidationSecureServerCA.crt
# COMODORSAAddTrustCA.crt
# の 3 つを、この順に結合する。
# 例)
# cat {your domain}.crt \
# COMODORSADomainValidationSecureServerCA.crt \
# COMODORSAAddTrustCA.crt \
# > server.crt
#
# Let's Encrypt なら fullchain.pem を指定する。
ssl_certificate /path/to/fullchain.pem;

# 証明書に対応する秘密鍵を指定。
ssl_certificate_key /path/to/privkey.pem;

# RSA は WEAK と判定される。
ssl_ciphers 'HIGH !aNULL !eNULL !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED !RSA';

# Cipher Strength を 100 にするためには AES128 など 128bit 以下の暗号を除外する必要がある。
#ssl_ciphers 'HIGH !aNULL !eNULL !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED !RSA !AES128';

# OpenSSL v1.1.1 以上では !ARIA128 も追加で除外する必要がある。
# OpenSSL v1.1.1 以上で、かつ TLSv1.3 を有効にした場合、 /etc/ssl/openssl.cnf にも修正が必要 (後述) 。
#ssl_ciphers 'HIGH !aNULL !eNULL !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED !RSA !AES128 !ARIA128';

# 2019-08-31 時点、 CBC 暗号スイートが WEAK と判定される。 (ただしスコアに影響はでない)
# CBC を除外するには !SHA1 !SHA256 !SHA384 を追加する。
# なお GCM や CHACHA20 暗号スイートの中にも SHA* という名前を持つものがあるが、これらは除外されない。
# 参考: https://security.stackexchange.com/a/207812
# CBC が WEAK となった経緯: https://security.stackexchange.com/questions/210072/why-does-ssl-labs-now-consider-cbc-suites-weak
#ssl_ciphers 'HIGH !aNULL !eNULL !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED !RSA !AES128 !ARIA128 !SHA1 !SHA256 !SHA384';

# DHE 暗号方式のためのパラメータファイルを指定する。
# このファイルは `openssl dhparam 2048 -out dhparam.pem` コマンドで生成できる。
# 2048bit 以上が推奨されている。
# A 評価にするためには 2048bit 以上で作成する必要がある。
# Key Exchange を 100 にするためには 4096bit 以上で作成する必要がある。
ssl_dhparam /path/to/dhparam.pem;

# Key Exchange を 100 にするためには secp384r1 を指定することが必要。
# 指定しなくても A (A+) 評価は可能。
#ssl_ecdh_curve secp384r1;

# どの暗号化スイートを使うか、サーバ側が決定するようにする。
# クライアントが脆弱な暗号化スイートを使おうとすることを防ぐ。
ssl_prefer_server_ciphers on;

# ssl_protocols を絞ると、対応するブラウザが減る。
# https://ja.wikipedia.org/wiki/Template:%E3%82%A6%E3%82%A7%E3%83%96%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8BTLS/SSL%E3%81%AE%E5%AF%BE%E5%BF%9C%E7%8A%B6%E6%B3%81%E3%81%AE%E5%A4%89%E5%8C%96
# A 評価にするなら TLSv1 以上に。(ほとんどの携帯電話が非対応に)
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

# Protocol Support を 100 点にするなら TLSv1.2 のみに。 (IE10 以下や Android 4 以下などが非対応に)
# OpenSSL v1.1.1 以上であれば、 TLSv1.3 を追加しても良い。
#ssl_protocols TLSv1.2;

# Session resumption (caching) を有効にする。
ssl_session_cache shared:SSL:10m;

# OCSP Staplingを有効にする。
# OpenSSL のバージョンが古すぎると、 "ssl_stapling" ignored というエラーが出る。
# OpenSSL のバージョンをアップデートすること。
# 例えば CentOS 5 だとアップデートしきっても古いままなので、有効にはできない。
ssl_stapling on;
ssl_stapling_verify on;

# OCSP Stapling で使用する証明書。
# 中間証明書~ルート証明書の順に結合したファイルを指定する。
# COMODO の PositiveSSL ならば、送付される証明書に含まれる
# COMODORSADomainValidationSecureServerCA.crt
# COMODORSAAddTrustCA.crt
# AddTrustExternalCARoot.crt
# の 3 つを、この順に結合する。
# 例)
# cat COMODORSADomainValidationSecureServerCA.crt \
# COMODORSAAddTrustCA.crt \
# AddTrustExternalCARoot.crt \
# > trusted.crt
#
# Let's Encrypt なら chain.pem を指定する。
ssl_trusted_certificate /path/to/chain.pem;


OpenSSL >= v1.1.1 かつ TLS v1.3 を有効にした状態で満点を取得する場合

OpenSSL v1.1.1 で TLS v1.3 を有効にすると


  • TLS_AES_256_GCM_SHA384

  • TLS_CHACHA20_POLY1305_SHA256

  • TLS_AES_128_GCM_SHA256

という暗号スイートが TLS v1.3 用に追加されます。

このうち TLS_AES_128_GCM_SHA256 は鍵長が 128bit であるため、 Cipher Strength のスコアが落ちます。

TLS_AES_128_GCM_SHA256 を無効化すればいいのですが、 nginx からでは TLS v1.3 用の暗号スイートを設定できません。

※ TLS v1.3 で使用される暗号スイートは、それ以外の暗号スイートと全く別に管理されるため、 ssl_ciphers で設定できません。

※ 2018-12-05 nginx v1.15.7 時点。

TLS v1.3 用の暗号スイートは openssl.cnf (通常 /etc/ssl/openssl.cnf にあるはず) を修正することで設定できます。

openssl.cnf を開き、最上部に以下の通り追記します。

openssl_conf = default_conf

[default_conf]
ssl_conf = ssl_sect

[ssl_sect]
system_default = system_default_sect

[system_default_sect]
ciphersuites = TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384

ciphersuites に TLS v1.3 で使用する暗号スイートを列挙します。

修正後、 nginx を再起動 (reload ではなく) することで適用されます。

参考: #1529 (Could not configure TLS1.3 ciphers in OpenSSL 1.1.1 pre4) – nginx

なおスコアには関係しませんが、 AES より ChaCha のほうがパフォーマンスが良いらしいので (未検証) 、 ChaCha を先に指定しています。

参考: #1445 (OpenSSL - ChaCha prioritized - Nginx enhancement) – nginx

参考: Use ChaCha only if prioritized by clnt by tmshort · Pull Request #4436 · openssl/openssl

参考: Do the ChaCha: better mobile performance with cryptography


TLS_AES_128_GCM_SHA256 無効化について

TLS_AES_128_GCM_SHA256 を無効化すると、 Qualys SSL Server Test で以下のようなエラーメッセージが表示されます。(2019-04-04 時点)


This server does not support the mandatory cipher suite TLS_AES_128_GCM_SHA256 for TLS 1.3. See RFC 8446 for details.


これは、 TLS v1.3 の仕様上 TLS_AES_128_GCM_SHA256 が必須であるためです。

しかし、 TLS_AES_128_GCM_SHA256 が有効だと Cipher Strength のスコアは落ちます。

このあたりは調整中らしいです。(See https://github.com/ssllabs/ssllabs-scan/issues/636#issuecomment-461727819)

仕様を正しく実装することのほうが重要かと思いますので、満点を目指すのはやはり趣味・実験・自己満足に収めたほうがいいかと思います。