本当にどうでもいいメモなどをたまに書く程度。

nginx で SSL Server Test 満点の設定 (2018/07/24)

日付:
タグ:

HiFormance の VPS をぼちぼち実運用に供してみる準備編。

ずっと Apache を (というよりは Apache の ITK MPM を) 愛用してきたのだが、

  • Apache 2.4.27 以降で mod_http2 が prefork MPM で動作しなくなる
  • Debian stretch 収録の Apache は 2.4.25 だが、mod_http2 のパッチバックポートにより 2.4.25 でも mod_http2 が prefork MPM で動作しなくなった
  • (ITK MPM は prefork MPM に依存している)

こんな感じで ITK MPM に未来がなくなってきてしまった。

というわけでまだ実運用に供していない環境で nginx を扱ってみることにした。


せっかくなので SSL Server Test で満点をとれる設定を目指してみたい。

まず nginx 自体を Debian 収録バージョンではなく、nginx 公式のリポジトリで mainline を入れる。

% echo 'deb http://nginx.org/packages/debian/ stretch nginx' | sudo tee /etc/apt/sources.list.d/nginx.list
% wget https://nginx.org/packages/keys/nginx_signing.key -O - | sudo apt-key add -
% sudo apt update
% sudo apt install nginx

Let's Encrypt の証明書を RSA 4096 bit で作る。

% sudo certbot certonly --agree-tos -n --rsa-key-size 4096 --webroot -w /var/www/html -w webmaster@example.com -d example.com

nginx の SSL まわりの設定。
いつも通りだが、Mozilla SSL Configuration Generator を基本にする。
nginx 1.15.1、OpenSSL 1.1.0f、Modern 設定。

結論からいくと、下記の設定で満点になる。

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	root /var/www/html;
	ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
	ssl_session_timeout 1d;
	ssl_session_cache shared:SSL:50m;
	ssl_session_tickets off;
	ssl_dhparam /etc/nginx/dhparam.pem;
	ssl_ecdh_curve secp384r1;
	ssl_protocols TLSv1.2;
	ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256';
	ssl_prefer_server_ciphers on;
	add_header Strict-Transport-Security max-age=15768000;
	ssl_stapling on;
	ssl_stapling_verify on;
	resolver 127.0.0.1 valid=300s;
}

(resolver が 127.0.0.1 なのは、私はそこに Unbound を動かしているため)

変更点は次のとおり。

  • ssl_ecdh_curve secp384r1;
  • ssl_ciphers から ECDHE-RSA-AES128-GCM-SHA256、ECDHE-RSA-AES128-SHA256 を除去。

ただしこの設定では、Android 5/6、Windows Phone 8.1 の IE11 からはアクセスできなくなっている。


Mozilla SSL Configuration Generator の出力では ssl_ciphers はこうなっていた。

ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';

この設定だと SSL Server Test は A+ 100/100/100/90 で、Cipher Strength だけ満点が取れず90点。
Android 4.x 以降でアクセスできる、まずまずまともな対応範囲といえる。

  • ECDHE-RSA-AES128-GCM-SHA256 を削ると、Android 5.x/6.x が NG になる。
  • ECDHE-RSA-AES128-SHA256 も削ると Cipher Strength が満点になるが、Windows Phone 8.1 の IE11 が NG になる。

メモ

  • Mozilla SSL Configuration Generator で Modern 設定の ssl_ciphers はもう全部 ECDHE-* なので、DH 鍵を 4096 bit で生成して ssl_dhparam を書いても意味ないっぽい。
  • ssl_ecdh_curve を指定しないと secp256r1 や x25519 の 256 bit (RSA 3072 bit 相当) になり、Key Exchange が90点になる。
  • 欲張って ssl_ecdh_curve secp521r1; なんてやると Chrome でアクセスできなくなる (Chrome というか BoringSSL が2015年に P-521 対応を削除したため)。ssl_ecdh_curve secp521r1:secp384r1; なら OK。

結論

古い環境の切り捨てがスコアに直結するとはいえ、さすがに Android 5.x/6.x はまだ合計 20% 以上のシェアがあって切り捨てられない。
副作用を考えると、結局 ssl_ciphers は Mozilla SSL Configuration Generator の通りにして A+ 100/100/100/90 で我慢するのが落としどころという感じ。

comments powered by Disqus