Let's EncryptのSSL証明書で、Qualys SSLTestでA+評価を獲得するには

前回の記事、[「Let's Encryptから無料・安全なSSL証明書を取得してNginxに設定するまで」](http://qiita.com/dseg/items/908c1b8aab19a5b26d92)で、[Let's Encrypt](https://letsencrypt.org/)から無料のSSL証明書を取得する方法を紹介しました。
Let's Encryptは、2015/11/17現在ベータ運用中ではありますが、だからと言って発行されたSSL証明書に問題がある訳ではありません。
その証拠に、Let's Encryptから取得したSSLサーバ証明書をnginxに設定し、Qualys SSL Testで検査したところ、無事「A+」評価が獲得できました。

![Assessment Result](https://raw.githubusercontent.com/dseg/dseg.github.io/master/img/qiita/03-ssltest/ssltest-aplus-digest.png)
(※役に立つかわかりませんが、[テスト結果の完全なスクリーンショット](https://raw.githubusercontent.com/dseg/dseg.github.io/master/img/qiita/03-ssltest/ssltest-full-test-result-shieldjp-17nov2015.png)も置いておきます。サイズが大きいので注意 / 0.5M)

ちょっと長いですが、設定の手順をご紹介します。
(どちらかというとQualys SSL TestでA+を取る設定についての一般的な話で、Let's EncryptのSSL証明書と余り関係のない内容になっていますが、話の流れとしてご容赦下さい)

# Qualys SSLTest とは?
米クオリス社によって提供されているサービスで、サイトのSSL設定をA～Fの分かりやすい点数で評価してくれます。
[digicert社によるSSLTestの解説](http://www.digicert.ne.jp/howto/basis/ssl_server_test.html)がとても参考になります。

また、私は使ったことはありませんが、API及びコマンドラインの公式クライアントも存在し、これを利用してサイトのSSLチェックの自動化等も可能なようです。
[Qualys SSLTest Command-line client](https://www.ssllabs.com/projects/ssllabs-apis/)

# 動作確認環境
- Ubuntu 14.04 Trusty 64-bit on AWS
- nginx (OpenResty) 1.9.3 + HTTP/2 patch
- Chrome 46.0.2490.86
- [Qualys SSLTest](https://www.ssllabs.com/ssltest/analyze.html) : SSL Report v1.20.28

# 設定項目

1. 弱い暗号形式の無効化
2. Logjam攻撃対策
3. OCSPステープリングの有効化
4. HSTSヘッダー対応 (要注意)
5. HPKP設定 (要注意)
6. 透かし入り証明書(Certificate Transparency)設定

# 設定内容
## 1.弱い暗号形式の無効化

この設定項目が一番重要です。この内容如何で、評価は「F」から「A」に上がります。
弱い暗号形式を無効化すれば評価値は上がりますが、その反面、古い環境(例:IE6/XP)で暗号化通信が出来なくなるなどの影響も出ますから、評価値に過度に捉われず、実現したい環境にあった設定するのが良いかと。
### 設定例

```nginx:nginx.conf
   #    ssl_ciphers  HIGH:!aNULL:!MD5;
      ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES
256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES
128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-
SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES
256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GC
M-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES
-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
      ssl_prefer_server_ciphers  on;
```

## 2.Logjam攻撃対策
[Logjam](https://weakdh.org/)攻撃に対応するため、DH鍵交換に利用する素数のビット数を2,048以上に設定します。

参考: [DH鍵交換に存在する脆弱性「Logjam」、HTTPSなどのプロトコルに影響](http://blog.trendmicro.co.jp/archives/11482)

### 設定例

```bash
# DHPARAMの生成
sudo openssl dhparam -out /etc/ssl/private/dhparams_4096.pem 4096
```

```nginx:nginx.conf
ssl_dhparam /etc/ssl/private/dhparams_4096.pem;
```

## 3.OCSPステープリングの有効化

### 設定例

```nginx:nginx.conf
  # Enable OCSP (Online Certificate Status Protocol) Stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 8.8.4.4 8.8.8.8 valid=300s;
  resolver_timeout 10s;
```

### 参考
- [nginxでのOCSP Stapling対応設定](http://blog.mylibs.jp/archives/173)

## 4.HSTSヘッダー対応 (要注意)
サイトにて、http:// にアクセスされた際、https:// に自動で切り替わって欲しい事を、ブラウザに知らせます。
このように動作すると困る・迷惑、という場合は、設定しないように気をつけましょう。

### 設定例

```nginx:nginx.conf
  # Enable HSTS (HTTP Strict Transport Security)
  add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
```

## 5.HPKP設定 (要注意)
HPKP (HTTP 公開鍵ピンニング)設定ですが、これも要注意です。
鍵の紛失や盗難にあった等の際に、サイトがブラウザに「怪しい」認定され、そのままになる危険があるようです。
HPKPが不正な時のブラウザの表示については、[HPKP確認用のテストサイト](https://pinningtest.appspot.com/)で実際に見る事ができます。
![HPKP failed on Chrome](https://raw.githubusercontent.com/dseg/dseg.github.io/master/img/qiita/03-ssltest/browser-snapshot-when-hpkp-failed.png)

こちらの解説が大変参考になりました。
[公開鍵ピンニングについて](http://postd.cc/about-public-key-pinning)

内容を十分理解した上で設定しましょう。

設定する内容はシンプルで、HTTPSサーバの応答ヘッダに以下のような行を含めるだけです。
pin-sha256にBase64エンコードされた公開鍵の情報を含めます。
メインの鍵とバックアップの鍵の、2つの情報が必須です。

```http
Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubdomains][; report-uri="reportURI"]
```
バックアップの鍵はなかったらこの機会に生成しましょう。
### 設定例

```bash
# メインの鍵の情報を取り出して、Base64符号化
sudo openssl x509 -noout -in /etc/letsencrypt/live/shield.jp/chain.pem -pubkey | \
  openssl pkey -pubin -outform der |\
  openssl dgst -sha256 -binary |\
  base64

# バックアップ鍵の生成
sudo openssl genrsa -out /etc/letsencrypt/live/shield.jp/hpkp-backup.key 4096
sudo openssl req -new -key /etc/letsencrypt/live/shield.jp/hpkp-backup.key -sha256 -out /etc/letsencrypt/live/shield.jp/hpkp-backup.csr

# バックアップ鍵の情報を取り出して、Base64符号化
sudo openssl req -pubkey < /etc/letsencrypt/live/shield.jp/hpkp-backup.csr | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
```

```nginx:nginx.conf
  # HPKP Settings
  add_header Public-Key-Pins 'pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; pin-sha256="GO8LTsdpBf/sLaUQ3/NPVCaGzZOmku+6iIBdeef9K2k="; max-age=2592000; includeSubDomains';
```

### 参考
- [公開鍵ピンニングについて](http://postd.cc/about-public-key-pinning)
- [Public Key Pinning Extension for HTTP@MDN](https://developer.mozilla.org/ja/docs/Web/Security/Public_Key_Pinning)
- [不正なSSL証明書を見破るPublic Key Pinningを試す](http://d.hatena.ne.jp/jovi0608/20140902/1409635279)

## 6.透かし入り証明書 (Certificate Transparency) 設定
[Symantec社によるCertificate Transparency（透かし入り証明書）の解説](https://www.symantec.com/ja/jp/page.jsp?id=ssl-certificate-transparency)が分かりやすいです。

設定しないとChromeで見た時に以下のように表示されるのが気になるため、設定することに決めたものの・・・、
![No CT provided](https://raw.githubusercontent.com/dseg/dseg.github.io/master/img/qiita/03-ssltest/no-ct-twitter.png)
(Twitterでも設定していない位なので、問題ないのかもしれませんが)

この設定はかなり面倒でした。

- nginxにnginx-ctプラグインをインストール
- ct-submitコマンドの取得とビルド
- CT情報の登録とSCTの取得

まずはnginxに[nginx-ct](https://github.com/grahamedgecombe/nginx-ct)プラグインをインストールします。
nginxのプラグインは動的に追加できないので、nginxをソースから再設定・リビルドする必要があります。

```bash
# nginx-ctプラグインのダウンロード
cd /tmp
wget https://github.com/grahamedgecombe/nginx-ct/archive/master.zip
unzip master.zip

# nginx-ctプラグインを有効化してNginxを再ビルド (OpenSSL 1.0.2以降も必要)
cd /usr/local/src/nginx-1.9.3
./configure --add-module=/tmp/nginx-ct-master --with-http_ssl_module  --with-openssl=/usr/local/src/openssl-1.0.2d
make clean
make
sudo make install
```

次に、透かし入り証明書情報(Certificate Transparency)をサーバに登録・取得するためのコマンド、[ct-submit](https://github.com/grahamedgecombe/ct-submit)をインストールします。
ビルドのために[Go](http://golang.org/)も必要です。

```bash
cd /tmp
wget https://github.com/grahamedgecombe/ct-submit/archive/master.zip
unzip master.zip
cd ct-submit-master
go build
sudo install -s -m755 ./ct-submit-master /usr/local/bin/ct-submit
```

今度はct-submitコマンドを使って、X509証明書情報をCTログサーバに送信します。
※スクリプト先頭部分のKEY, CTSUBMIT, SCTS_DIR, $SCTS_DIRの許可件等は適切に調整して下さい。
(一部のサーバからはエラーが返ってきたのでコメントアウトしました)

```bash:ct-submit.sh
#!/bin/sh
# Let's encryptから入手した fullchain.pem の場所
KEY=/etc/letsencrypt/live/shield.jp/fullchain.pem
# CT情報の保存先
SCTS_DIR=/etc/letsencrypt/live/shield.jp/scts
# ct-submitコマンド
CTSUBMIT=/usr/local/bin/ct-submit

echo 1
sudo sh -c "$CTSUBMIT ct.googleapis.com/aviator \
<$KEY \
>$SCTS_DIR/aviator.sct"
echo 2
sudo sh -c "$CTSUBMIT ct.googleapis.com/pilot \
<$KEY \
>$SCTS_DIR/pilot.sct"
echo 3
sudo sh -c "$CTSUBMIT ct.googleapis.com/rocketeer \
<$KEY \
>$SCTS_DIR/rocketeer.sct"
#echo 4
#sudo sh -c "$CTSUBMIT ct1.digicert-ct.com/log \
#<$KEY \
#>$SCTS_DIR/digicert.sct"
#echo 5
#sudo sh -c "$CTSUBMIT ct.izenpe.com \
#<$KEY \
#>$SCTS_DIR/izenpe.sct"
#echo 6
#sudo sh -c "$CTSUBMIT log.certly.io \
#<$KEY \
#>$SCTS_DIR/certly.sct"
```

指定したディレクトリにSCTファイルが出力されているか確認しましょう。
（SCT = Signed Certificate Timestamp：登録済み証明書タイムスタンプ）

```bash
sudo ls -al /etc/letsencrypt/live/shield.jp/scts
total 20
drwxr-xr-x 2 root root 4096 Nov 12 16:54 .
drwxr-xr-x 3 root root 4096 Nov 12 17:46 ..
-rw-r--r-- 1 root root  118 Nov 12 16:54 aviator.sct
-rw-r--r-- 1 root root  119 Nov 12 16:54 pilot.sct
-rw-r--r-- 1 root root  118 Nov 12 16:54 rocketeer.sct
```

以上で完了です。
あとはnginxを再起動してから、ChromeでHTTPSのURLにアクセスし、アドレスパーの緑の部分をクリックして確認しましょう。
以下のように、サーバからCT情報が提供されたということが表示されればOKです。

![CT Provided](https://raw.githubusercontent.com/dseg/dseg.github.io/master/img/qiita/03-ssltest/chrome-dialog-ct-supplied-from-server.png)
### 設定例

```nginx:nginx.conf
  # nginx-ctプラグインを有効にしていないとエラーになります
  ssl_ct on;
  ssl_ct_static_scts /etc/letsencrypt/live/shield.jp/scts;
```
### 参考
- [Certificate Transparency - Resource for Site Owners - nginx](http://www.certificate-transparency.org/resources-for-site-owners/nginx)
- [Certificate Transparency（CT：透かし入り証明書）とは - Digicert社による解説](https://www.digicert.ne.jp/howto/basis/ct/)

Certificate Transparentのログサーバの一覧はどこから手に入る?
http://esupport.trendmicro.com/media/13357571/CT%20FAQ.pdf
答: 以下から入手可能。
http://www.certificate-transparency.org/known-logs

# 最後に

改めてnginx.confの設定内容です。少しずつ紹介した項目をひとまとめにしました。

```nginx:nginx.conf
    #
    # HTTPS server
    #
    server {

      listen       443 ssl http2; # HTTP/2パッチを当てているため。通常は http2 は不要
      server_name  shield.jp;

      ssl_certificate /etc/letsencrypt/live/shield.jp/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/shield.jp/privkey.pem;

      ssl_session_cache   shared:SSL:3m;
      ssl_buffer_size     8k;
      ssl_session_timeout 10m;

      # 1. 暗号方式の設定
      # (デフォルト) ssl_ciphers  HIGH:!aNULL:!MD5;
      ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
      ssl_prefer_server_ciphers  on;

      # 2. Logjam攻撃対策
      ssl_dhparam /etc/ssl/private/dhparam_4096.pem;

      # 3. Enable OCSP (Online Certificate Status Protocol) Stapling
      ssl_stapling on;
      ssl_stapling_verify on;
      resolver 8.8.4.4 8.8.8.8 valid=300s;
      resolver_timeout 10s;

      # 4. Enable HSTS (HTTP Strict Transport Security)
      add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";

      # 5. HPKP Settings
      add_header Public-Key-Pins 'pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; pin-sha256="GO8LTsdpBf/sLaUQ3/NPVCaGzZOmku+6iIBdeef9K2k="; max-age=2592000; includeSubDomains';

      # 6.証明書透かし (Certificate Transparency)設定
      ssl_ct on;
      ssl_ct_static_scts /etc/letsencrypt/live/shield.jp/scts;

      # 後略
}
```

これでようやくセキュアなWebサイトが完成しました。
やったね!
・・・それにしても疲れます。
更に、SSLの一つ一つの設定項目を吟味するとなると、膨大な背景知識の網羅が必要となり気が遠くなってきます。
Qualys SSLTestのおかげで、現状安全かどうかをまずは確認できる訳で、本当に有難いサービスだなと実感しました。
素晴らしいサービスの開発者に感謝です。

# 参考

- [我々はどのようにして安全なHTTPS通信を提供すれば良いか](http://qiita.com/harukasan/items/fe37f3bab8a5ca3f4f92)

- [Webサーバー nginx における SSL証明書設定の安全性向上 ～SSL Server Test で A+ 判定を目指して～](https://www.saintsouth.net/blog/safety-of-ssl-certificate-setting-improvements-in-web-server-nginx-to-get-rankaplus-from-ssl-server-test/)

- [Strong Ciphers for Apache, nginx and Lighttpd](https://cipherli.st/)
※Apache等定番サーバの具体的な設定例のまとめ。古いブラウザ用の設定も用意されている
(ページ内の「Yes, give me a ciphersuite that works with legacy / old software」リンクをクリックすると切り替わる)

- [Strong SSL Security on nginx](https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html)

- [Getting an A+ rating on the Qualys SSL Test](https://scotthelme.co.uk/a-plus-rating-qualys-ssl-test/)
nginxでの設定例

- [HOWTO: A+ with all 100%’s on SSL Labs test using apache2.4 (READ WARNINGS)](https://community.letsencrypt.org/t/howto-a-with-all-100-s-on-ssl-labs-test-using-apache2-4-read-warnings/2436)