我々はどのようにして安全なHTTPS通信を提供すれば良いか

HTTPS通信は複数のプロトコル、手法が組み合わされて実現されている。そのため、暗号化手法それぞれのリスク、ブラウザの対応等様々な用件があり、全てを理解するにはちょっと時間とリソースが足りない。結局のところ、我々はどのようにして安全なHTTPS通信を提供できるのか。色々調べていたところ、MozillaがMozilla Web siteに使用する、HTTPSの推奨設定を公開している。

- [Security/Server Side TLS - MozillaWiki
](https://wiki.mozilla.org/Security/Server_Side_TLS)

このドキュメントはMozillaのサーバ運用チームが、Mozillaのサイトをより安全にするために公開しているもので、他のサイトにそのまま適用できるかは十分に注意する必要がある。例えばガラケー向けサイトとか。そのまま使えないとしても、HTTPS通信の設定をどうすれば良いか、理解の一助になるはずだ。

この記事は上記MozillaWikiの抄訳に基づいて構成している。また、記事中の翻訳部分は引用形式で示している。


# 推奨される設定

> 推奨される **設定** には3種類ある。閲覧者にあわせて適切な設定を選択すること。
> あなたが後方互換性が必要なく、モダンなクライアントにだけ向けてサービスを構築している場合、Modern設定を使用すること。そうでなければ、Intermediate設定が推奨される。
> WindowsXP IE 6や、古いボット、ライブラリ等のとても古いクライアントによってアクセスされるのであれば、Old設定を使用すること。
>
> | **設定** | 対応するクライアントの最小バージョン |
> |:---------|:-------------------|
> | Modern   | Firefox 27, Chrome 22, IE 11, Opera 14, Safari 7, Android 4.4, Java 8 |
> | Intermediate | Firefox 1, Chrome 1, IE 7, Opera 5, Safari 1, WindowsXP IE8, Android 2.3, Java7 |
> | Old | Windows XP IE6, Java 6 |
> 
>
> ## Modern
>
> - **Ciphersuite:** ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
> - **Versions:** TLSv1.1, TLSv1.2
> - **RSA key size:** 2048
> - **DH Parameter size:** 2048
> - **Elliptic curves:** secp256r1, secp384r1, secp521r1 (at a minimum)
> - **Certificate signature:** SHA-256
>
> ## Intermediate
> 
> - **Ciphersuite:** ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
> - **Versions:** TLSv1, TLSv1.1, TLSv1.2
> - **RSA key size:** 2048
> - **DH Parameter size:** 2048
> - **Elliptic curves:** secp256r1, secp384r1, secp521r1 (at a minimum)
> - **Certificate signature:** SHA-256
>
> ## Old
>
> - **Ciphersuite:** ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
> - **Versions:** SSLv3, TLSv1, TLSv1.1, TLSv1.2
> - **RSA key size:** 2048
> - **DH Parameter size:** 1024
> - **Elliptic curves:** secp256r1, secp384r1, secp521r1
> - **Certificate signature:** SHA-1 (windows XP pre-sp3 is incompatible with sha-256)
>
> もしOpenSSLのバージョンが古い場合、使用できない暗号方式は無視される。上記全ての暗号化方式を常に使用し、OpenSSLが対応するものを選択できるようにするようにすること。
>
> 暗号化スイートの順番は非常に重要であり、アルゴリズム選択の優先度を指定する。上記の推奨設定はPFS(perfect forward secrecy)を提供するアルゴリズムを優先している。アプリケーションのために手動で選択する必要がある場合は、この順序を維持する必要がある。

![HTTPSの概略](https://qiita-image-store.s3.amazonaws.com/0/5218/6aba3cf0-8674-8b79-8d1a-31d628dd7c8c.png)

HTTPS通信の概略を図に示した。HTTPS通信では、まずサーバ側で生成した秘密鍵と公開鍵を用いる **(1)**。公開鍵は、署名要求(CSR)として認証局に送付し、署名を行うことで、クライアントが正当性を確認できるようにしておく **(2)**。署名された公開鍵は、サーバ証明書として、サーバに保存する。

クライアントからリクエストが行われた場合 **(3)**、保存しておいたサーバ証明書をクライアントに送付する **(4)**。クライアントはインストールされているルート証明書を用いて正当性を検証、失効していないかを確認する。次に暗号化通信に用いる共通鍵を生成、共通鍵は公開鍵で暗号化してサーバに送付する **(5)**。サーバは予め持っている秘密鍵を用いてこれを復号し(鍵交換) **(6)**、共通鍵による暗号化通信を行う **(7)**。

ここで示した公開鍵を生成する鍵認証方式(Au)、メッセージ認証符号方式(Mac)、鍵交換方式(Kx)、共通鍵による暗号化通信方式(Enc)の組み合わせを **暗号化スイート** と呼ぶ。暗号化スイートには複数の種類があり、サーバでどの暗号化スイートを使用できるかを指定する。

暗号化スイートの指定は、コロン`:`で区切って指定する。これが前述した暗号化スイートのリストになる。暗号化スイートの順番は、使用する優先度を示し、使用できるものが、前から順番に選択される。また、先頭にエクスクラメーション`!`を付けたものは、使用できないことを示す。

上記リストを指定した時に、使用できる暗号化スイートは次のコマンドで確認できる (下記はOpenSSL 1.0.1の例)。

```
$ openssl ciphers -V 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'|column -t
0xC0,0x2F  -  ECDHE-RSA-AES128-GCM-SHA256    TLSv1.2  Kx=ECDH  Au=RSA    Enc=AESGCM(128)    Mac=AEAD
0xC0,0x2B  -  ECDHE-ECDSA-AES128-GCM-SHA256  TLSv1.2  Kx=ECDH  Au=ECDSA  Enc=AESGCM(128)    Mac=AEAD
0xC0,0x30  -  ECDHE-RSA-AES256-GCM-SHA384    TLSv1.2  Kx=ECDH  Au=RSA    Enc=AESGCM(256)    Mac=AEAD
0xC0,0x2C  -  ECDHE-ECDSA-AES256-GCM-SHA384  TLSv1.2  Kx=ECDH  Au=ECDSA  Enc=AESGCM(256)    Mac=AEAD
0x00,0x9E  -  DHE-RSA-AES128-GCM-SHA256      TLSv1.2  Kx=DH    Au=RSA    Enc=AESGCM(128)    Mac=AEAD
0x00,0xA2  -  DHE-DSS-AES128-GCM-SHA256      TLSv1.2  Kx=DH    Au=DSS    Enc=AESGCM(128)    Mac=AEAD
0x00,0xA3  -  DHE-DSS-AES256-GCM-SHA384      TLSv1.2  Kx=DH    Au=DSS    Enc=AESGCM(256)    Mac=AEAD
0x00,0x9F  -  DHE-RSA-AES256-GCM-SHA384      TLSv1.2  Kx=DH    Au=RSA    Enc=AESGCM(256)    Mac=AEAD
0xC0,0x27  -  ECDHE-RSA-AES128-SHA256        TLSv1.2  Kx=ECDH  Au=RSA    Enc=AES(128)       Mac=SHA256
0xC0,0x23  -  ECDHE-ECDSA-AES128-SHA256      TLSv1.2  Kx=ECDH  Au=ECDSA  Enc=AES(128)       Mac=SHA256
0xC0,0x13  -  ECDHE-RSA-AES128-SHA           SSLv3    Kx=ECDH  Au=RSA    Enc=AES(128)       Mac=SHA1
0xC0,0x09  -  ECDHE-ECDSA-AES128-SHA         SSLv3    Kx=ECDH  Au=ECDSA  Enc=AES(128)       Mac=SHA1
0xC0,0x28  -  ECDHE-RSA-AES256-SHA384        TLSv1.2  Kx=ECDH  Au=RSA    Enc=AES(256)       Mac=SHA384
0xC0,0x24  -  ECDHE-ECDSA-AES256-SHA384      TLSv1.2  Kx=ECDH  Au=ECDSA  Enc=AES(256)       Mac=SHA384
0xC0,0x14  -  ECDHE-RSA-AES256-SHA           SSLv3    Kx=ECDH  Au=RSA    Enc=AES(256)       Mac=SHA1
0xC0,0x0A  -  ECDHE-ECDSA-AES256-SHA         SSLv3    Kx=ECDH  Au=ECDSA  Enc=AES(256)       Mac=SHA1
0x00,0x67  -  DHE-RSA-AES128-SHA256          TLSv1.2  Kx=DH    Au=RSA    Enc=AES(128)       Mac=SHA256
0x00,0x33  -  DHE-RSA-AES128-SHA             SSLv3    Kx=DH    Au=RSA    Enc=AES(128)       Mac=SHA1
0x00,0x40  -  DHE-DSS-AES128-SHA256          TLSv1.2  Kx=DH    Au=DSS    Enc=AES(128)       Mac=SHA256
0x00,0x6B  -  DHE-RSA-AES256-SHA256          TLSv1.2  Kx=DH    Au=RSA    Enc=AES(256)       Mac=SHA256
0x00,0x38  -  DHE-DSS-AES256-SHA             SSLv3    Kx=DH    Au=DSS    Enc=AES(256)       Mac=SHA1
0x00,0x39  -  DHE-RSA-AES256-SHA             SSLv3    Kx=DH    Au=RSA    Enc=AES(256)       Mac=SHA1
0xC0,0x12  -  ECDHE-RSA-DES-CBC3-SHA         SSLv3    Kx=ECDH  Au=RSA    Enc=3DES(168)      Mac=SHA1
0xC0,0x08  -  ECDHE-ECDSA-DES-CBC3-SHA       SSLv3    Kx=ECDH  Au=ECDSA  Enc=3DES(168)      Mac=SHA1
0x00,0x9C  -  AES128-GCM-SHA256              TLSv1.2  Kx=RSA   Au=RSA    Enc=AESGCM(128)    Mac=AEAD
0x00,0x9D  -  AES256-GCM-SHA384              TLSv1.2  Kx=RSA   Au=RSA    Enc=AESGCM(256)    Mac=AEAD
0x00,0x32  -  DHE-DSS-AES128-SHA             SSLv3    Kx=DH    Au=DSS    Enc=AES(128)       Mac=SHA1
0x00,0x3C  -  AES128-SHA256                  TLSv1.2  Kx=RSA   Au=RSA    Enc=AES(128)       Mac=SHA256
0x00,0x2F  -  AES128-SHA                     SSLv3    Kx=RSA   Au=RSA    Enc=AES(128)       Mac=SHA1
0x00,0x6A  -  DHE-DSS-AES256-SHA256          TLSv1.2  Kx=DH    Au=DSS    Enc=AES(256)       Mac=SHA256
0x00,0x3D  -  AES256-SHA256                  TLSv1.2  Kx=RSA   Au=RSA    Enc=AES(256)       Mac=SHA256
0x00,0x35  -  AES256-SHA                     SSLv3    Kx=RSA   Au=RSA    Enc=AES(256)       Mac=SHA1
0x00,0x0A  -  DES-CBC3-SHA                   SSLv3    Kx=RSA   Au=RSA    Enc=3DES(168)      Mac=SHA1
0x00,0x88  -  DHE-RSA-CAMELLIA256-SHA        SSLv3    Kx=DH    Au=RSA    Enc=Camellia(256)  Mac=SHA1
0x00,0x87  -  DHE-DSS-CAMELLIA256-SHA        SSLv3    Kx=DH    Au=DSS    Enc=Camellia(256)  Mac=SHA1
0x00,0x84  -  CAMELLIA256-SHA                SSLv3    Kx=RSA   Au=RSA    Enc=Camellia(256)  Mac=SHA1
0x00,0x45  -  DHE-RSA-CAMELLIA128-SHA        SSLv3    Kx=DH    Au=RSA    Enc=Camellia(128)  Mac=SHA1
0x00,0x44  -  DHE-DSS-CAMELLIA128-SHA        SSLv3    Kx=DH    Au=DSS    Enc=Camellia(128)  Mac=SHA1
0x00,0x41  -  CAMELLIA128-SHA                SSLv3    Kx=RSA   Au=RSA    Enc=Camellia(128)  Mac=SHA1
```

## 優先順位付けのロジック

> 1. ECDHE+AESGCM方式を最初に選択する。これらはTLS v1.2の暗号化方式で現在は広くサポートされていない。これらの暗号化方式をターゲットとした攻撃は現在のところ知られていない。
> 2. PFS暗号化スイートはECDHE、DHEの順番で優先される。
> 3. AES 128はAES 256よりも優先される。AES 256により向上するセキュリティがそのコストに見合うかどうかは議論がある。今のところAES 128が優先される。AES 128は十分なセキュリティを提供しとてもはやく、そして、タイミング攻撃にもより耐性があるように考えられる。
> 4. AESはRC4よりも優先される。AESのおけるBEAST攻撃はTLS v1.1以上では危険性が低減されており、TLS1.0で成功するのも難しい。対してRC4については低減されておらず、ますます危険になっている。
> 5. RC4は削除される予定であるが、互換性のために残されている。

上述した暗号化スイートリストの優先順位はこのロジックで決定されている。基本的には攻撃方法が発見されていない暗号化スイートが優先されており、最後に古いブラウザの互換性のため`RC4−SHA`を指定している。`RC4-SHA`はWindows XPのInternet Explorer 6においても動作し、現在使われている殆どのブラウザでサポートされている。

## 使用してはいけないもの

> - **aNULL**: 認証しないDH鍵交換であり、Man-In-The-Middle攻撃の対象になる。
> - **eNull**: 暗号化が行われず平文で通信される
> - **EXPORT**: レガシーで脆弱な暗号化方式であり、US法で輸出可能とされている。
> - **DES**: 非推奨なデータ暗号標準である。
> - **SSLv2**: SSL標準の古いバージョンで定義された暗号化方式であり、現在は非推奨である。
> - **MD5**: 非推奨なハッシュアルゴリズムである。

上記暗号化方式は脆弱であるため使用してはならず、使用しないようエクスクラメーション`!`を付けて指定する。

## Perfect Forward Secrecy (PFS)

> **FS** (Forward Secrecy) のコンセプトはシンプルである。クライアントとサーバはこれまでに捕らえられていない鍵を交換し、セッションの終了時に破棄する。サーバからのRSA秘密鍵はクライアント/サーバ間のDH鍵交換に使用される。DH鍵交換から得られたpre-master keyは暗号化に使用する。このpre-master keyはクライアント/サーバ間の接続に固有で、限られた時間だけで使用されエフェメラル(**Ephemeral**)と呼ばれる。

Forward Secrecy(あるいはPerfect Forward Secrecy, **PFS**)は、全ての暗号化された通信を記録しておき、後からある期間の鍵を盗んだことができたとしても、それ以前に記録しておいた通信は復号できないという性質を指す。鍵交換にECDHEまたはDHEを使っている場合はPFSであると言うことができる。なお、ECDHE/DHEのEは"Ephemeral"のEである。

## ブラウザが使用している暗号化スイートを確認する

Google Chromeでは、ロケーションバーの鍵マークをクリックし、接続タブで使用している暗号化スイートを確認できる。

![Google Chrome](https://qiita-image-store.s3.amazonaws.com/0/5218/17e429d3-d373-f0f2-0b7a-ef5c9b958a3f.png)

# OCSP Stapling

> サーバに接続する際、クライアントは証明書失効リスト(**CRL**)、またはOnline Certificate Status Protocol (**OCSP**)を使用してサーバー証明書の正当性を確認する必要がある。CRLの問題点は、リストが巨大化し、ダウンロードに非常に時間がかかることだ。
>
> OCSPは非常に軽量であり、単一のレコードのみを取得する。しかし、副作用として、サーバに接続する際、OCSP要求をサードパーティのOCSP responderに対して行わなければならず、レイテンシと失敗する可能性が加わる。実際、認証局が運用しているOCSP responderでは、しばしばレスポンスが得られず、ブラウザがなにも表示せず失敗してしまい信頼性が低い。
>
> これを解決するために、TLSハンドシェイク中にキャッシュされたOCSPレコードをサーバが送信することを可能にする。これによりOCSP responderをバイパスすることになる。これにより、クライアントとOCSP responder間のラウンドトリップ時間をなくすことができ、この機能はOCSP Staplingと呼ばれる。

OCSPとはサーバ証明書が正当なものか確認するためのプロトコルで、証明書が失効していないかどうかOCSP responderに確認する仕組みである。従来は署名所失効リスト(CRL)が利用されていたが、現在ではOCSPを利用して証明書の失効を確認するのが一般的になっている。

### OCSP
![図:OCSP](https://qiita-image-store.s3.amazonaws.com/0/5218/86136663-b3e1-4919-b62e-4438ab3d9201.png)

HTTPS通信を行う際には、証明書が失効していないか確認するため、OCSP Responderに証明書の正当性を確認する必要がある。HTTPS通信の度にOCSPリクエストを行わなければならないため、OCSP responderが低速であった場合、大きなボトルネックになる。

### OCSP Stapling
![図:OCSP Stapling](https://qiita-image-store.s3.amazonaws.com/0/5218/faca646b-4e30-eadd-428a-d7b0f032a436.png)

OCSP Stapling(ステープリング)では、クライアントがOCSP要求を行うのではなく、サーバがOCSP要求を行い、その応答をキャッシュする。サーバはキャッシュしたOCSP応答を、サーバ証明書と一緒にクライアントに送信する。これによりクライアントはOCSP responderに問い合わせる必要がなくなり、HTTPS通信の開始を高速化することができる。

## OCSP Staplingが有効であることを確認する

OCSP Staplingが有効かどうかはopensslコマンドにより確認することができる。
Staplingが有効である場合は、レスポンスにOCSP情報が含まれる。

#### OCSP応答が含まれている場合

OCSP応答が含まれている場合には、`OCSP Response Data`が出力される。

```
$ openssl s_client -connect www.secure.pixiv.net:443 -tls1 -status | head
depth=1 /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - G2
verify error:num=20:unable to get local issuer certificate
verify return:0
CONNECTED(00000003)
OCSP response:
======================================
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: 69261190F2EC0E5D1AFF26647099C131512D26FB
    Produced At: May 19 04:31:13 2014 GMT
    Responses:
^C
```

#### OCSP応答が含まれていない場合

OCSP応答が含まれていない場合には、`OCSP response: no response sent`が出力される。

```
$ openssl s_client -connect www.secure.pixiv.net:443 -tls1 -status | head
depth=1 /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - G2
verify error:num=20:unable to get local issuer certificate
verify return:0
CONNECTED(00000003)
OCSP response: no response sent
...
^C
```

# 推奨されるサーバー設定

ここではMozillaWikiで推奨とされているサーバ設定について紹介する。前述したとおり、これはMozilla関連サイトにおける推奨設定であり、全ての場合に当てはまる場合ではないことに注意。

設定のポイントは次の通りだ。

- 前述したIntermediate設定を使用している
- WindowsXP Internet Explorer 6はサポートしない
- SSlv2、SSLv3は使用しない
- サーバが提供する暗号化スイートを優先的に使用するようにする
- 使用する暗号化スイートには上述した暗号化スイートを指定する
- OCSP Staplingを有効にする
- SSL Compressionは無効にする

## nginxの設定例

```nginx.conf

server {
    listen 443 ssl;

    ssl_certificate /path/to/証明書と中間証明書を結合したファイル.cert.concat;
    ssl_certificate_key /path/to/秘密鍵.key;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

    ssl_prefer_server_ciphers on; # サーバ側の暗号化スイートを優先する
    ssl_dhparam /path/to/dhparam.pem; # DHE暗号化スイートのためのパラメータファイル

    # OCSP Staplingを有効にする 
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /path/to/ルートCA証明書と中間証明書を結合したファイル;

    # OCSP StaplingのためにはDNSリゾルバを指定する必要がある
    resolver <IP DNS resolver>;

    # HSTSヘッダを出力する場合は、下記を設定する (recommended, but be careful)
    # alwaysを指定すると異常系(404)などのステータスでもHSTSヘッダを出力するようになる
    # add_header Strict-Transport-Security "max-age=31536000; includeSubdomains" always;
 
    # ... snip ...

}
```

### ssl_protocols

使用するプロトコルを明示的に指定する。ここでは、TLSv1、TLSv1.1、TLSv1.2、そして互換性のためにSSLv3を指定している。SSLv3は古いプロトコルであるため、将来的にはサポートの停止を検討しよう。TLSv1.1、TLSv1.2を使用するにはOpenSSL 1.0.1以上を使用する必要がある。

### ssl_ciphers

暗号化スイートのリストを指定する。

### ssl_prefer_server_ciphers

この設定を有効にしておくことで、クライアント側ではなくサーバ側の暗号化スイートが優先されるようになる。この指定がない場合、クライアントが標準で設定している弱い暗号化スイートが選択される可能性がある。

### ssl_dhparam

`ssl_dhparam`ディレクティブには、DH鍵交換に使用するパラメータファイルを指定する。このdhparam.pemはOpenSSLコマンドで生成できる。鍵長は2048bitが推奨されている。

```
$ openssl dhparam 2048 -out dhparam_20140516.pem
```

### ssl_stapling

nginxでは1.3.7以上でOCSP Stablingが使用できる。OCSP Staplingを有効にするには`ssl_stapling`を有効にし、証明書と中間証明書、そしてDNS解決に使用する`resolver`ディレクティブを指定する必要がある。この`resolver`に指定したDNSサーバはOCSP Responderの名前解決に使用される。

### ssl_session_ticket_key

nginx 1.5.7以上では、TLS Session Ticketsが有効にできる。Session Ticketsを有効にするには、鍵ファイルを`ssl_session_ticket_key`ディレクティブに指定する必要がある。

鍵ファイルは48バイトのランダムデータである必要があり、下記コマンドで生成できる。

```
$ openssl rand 48 > ticket.key
```

### gzip

HTTPS通信においてgzip圧縮通信を有効にしている場合、[BREACH攻撃](http://breachattack.com)の対象となる可能性がある。この攻撃では、レスポンスに含まれるCSRFトークンなどのシークレットトークンが盗聴される可能性がある。

この攻撃を根本的に防ぐためには、圧縮通信を無効にする必要があるが、実際には無効にできない場合も多いだろう。対策としては、CSRFトークンといったシークレットトークンの生成方法の変更が考えられる。

### HTTP Strict Transport Security(HSTS)

> HSTSはクライアントにHTTPSだけでサイトに接続することを伝えるHTTPヘッダである。これは、与えられたサイトに対するいかなるHTTP URLを無視するように伝えることで、セキュリティを強化する。この指定は、max-ageに指定した期間クライアントにキャッシュされる。

HSTSヘッダは、クライアントに対しすべてHTTPSで接続するように指示する。対応しているブラウザは、HTTP URLがあれば、HTTPSに変換してリクエストする。これにより、HTTPからHTTPSへのリダイレクトをクライアントに行わせることができるようになる。

## Apache HTTP Server

Apacheでは2.3.3以上でOCSP Staplingを利用できる。Apache 2.4.7より前のバージョンでは、DHパラメータは1024bitに固定されており、変更することができない。この問題はApache 2.4.7で修正され、RHEL6のApache2.2パッケージ(httpd-2.2.15-32.el6)にもバックポートされている。将来のバージョンでは、DHパラメータが自動的に選択されるようになる予定である。

Apache HTTP Serverの設定については詳しくないので、Mozilla Wikiの設定を示すにとどめておく。

```apache:httpd.conf
<VirtualHost *:443>

    # ... snip ...

    SSLEngine on
    SSLCertificateFile /path/to/signed_certificate
    SSLCertificateChainFile /path/to/intermediate_certificate
    SSLCertificateKeyFile /path/to/private/key
    SSLCACertificateFile /path/to/all_ca_certs
    SSLProtocol all -SSLv2 -SSLv3
    SSLCipherSuite <recommended ciphersuite from top of this page>
    SSLHonorCipherOrder on
    SSLCompression off

    SSLUseStapling on
    SSLStaplingResponderTimeout 5
    SSLStaplingReturnResponderErrors off
    SSLStaplingCache shmcb:/var/run/ocsp(128000)
 
    # ... snip ...

</VirtualHost>
```

### SSLProtocol

Apache 2.0/2.2ではSSL v2が、SSLv3が有効であるため、SSLProtocolディレクティブで無効にするよう指定する。

```apache
    SSLProtocol all -SSLv2 -SSLv3
```

Apache 2.4からはSSLv2は標準で無効になっている。

## Amazon Web Services Elastic Load Balancer (AWS ELB)

> ELBではTLS 1.2と暗号化スイートのオーダーをサポートしている。DHパラメータの変更と、OCSP Staplingはサポートされていない。
> ELBの標準設定は適切であり、WebコンソールまたはAPIから変更できる。それでも次のスクリプトを使用し、暗号化スイートを指定することを推奨する: https://github.com/mozilla/identity-ops/blob/master/aws-tools/apply_security_assurance_elb_ciphersuite_policy.py

ELBの標準ポリシーについては下記ページで確認することができる。

- [SSL Security Policy Table - Elastic Load Balancing](http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-security-policy-table.html)

# SSL Labs (Qualys)

> ここで利用できる: https://www.ssllabs.com/ssltest/
>
> Qualys SSL Labsは包括的なSSLテストスイートを提供している。
>
> GlobalSignはSSLラボのインターフェースを変更しており、こちらも興味深い: https://sslcheck.globalsign.com/

Qualys SSL LabsのSSL Testでは、セキュリティに関する診断、各クライアントのシミュレーション、各種設定の確認が行える。

- [Qualys SSL Labs - Projects / SSL Server Test](https://www.ssllabs.com/ssltest/)

# SPDY

> (see also http://en.wikipedia.org/wiki/SPDY and http://www.chromium.org/spdy/spdy-protocol)

> SPDYはTLSを組み込んだプロトコルであり、ページロード中の遅延を削減することを試みている。 今のところHTTP標準ではない(HTTP 2.0のdraftになっている)が、広くサポートされている。

> SPDY/3には[CRIME攻撃](http://zoompf.com/2012/09/explaining-the-crime-weakness-in-spdy-and-ssl)に対する脆弱性がある。これは圧縮を利用していることが原因である。現在、クライアントは脆弱性を回避するためにgzipを用いた非標準のハックを実装している。SPDY 4では適切な修正を含むことが計画されている。

SPDYはTLS上でページロードを削減するためのプロトコルであり、Google Chromeをはじめとし、広くサポートされている。現在のバージョンはSPDY/3.1であり、nginx v1.5.10からSPDY/3.1をサポートしている。

nginxでSPDYを使用するには、`listen`ディレクティブに`spdy`を追加する。

```nginx
server {
    # listenディレクティブにspdyを追加する
    listen 443 ssl spdy;

    # ... snip ...
}
```

# TLS tickets (RFC 5077)

> サーバとクライアント間でTLSハンドシェイクが一度行われたとき、それぞれセッションチケットを交換する。このチケットにはAES-CBC 128bitの鍵を含んでおり、セッションを復元することができる。鍵は一般に固定されており、Webサーバがリスタートしたときにのみ更新される(最近のApacheではファイルに格納されており、リスタート時にも保持される)。
>
> 現在のワークアラウンドはRFC5077サポートを無効にすることである。
> 
> 参照: https://media.blackhat.com/us-13/US-13-Daigniere-TLS-Secrets-Slides.pdf

TLSチケットとは、暗号化したセッション情報を含んだチケットクライアントに送付しておき、これをもとにTLSセッションを再開する機能である。TLSハンドシェイクを行ったとき、サーバはクライアントにTLSチケットを発行する。TLSチケットにはセッションデータが全て含まれている。このTLSチケットはサーバの持つ秘密鍵で暗号化されている。

クライアントは次回ネゴシエーション時、このTLSチケットをサーバに送信する。サーバは持っている秘密鍵で、チケットを復号化することで、前回のセッションを再開することができる。クライアントにデータが保存されるが、サーバだけが秘密鍵で暗号化されているため解読されることはない。Google、FacebookといったHTTPSが前提とされているサイトではTLS ticketが有効になっている。

TLSチケットが有効になっているかは前述した`openssl s_client`コマンドで確認できる。

```
$ openssl s_client -connect www.secure.pixiv.net:443 -tls1 -status | head
depth=1 /C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - G2
verify error:num=20:unable to get local issuer certificate
verify return:0
CONNECTED(00000003)

... snip ...

SSL-Session:
    ...
    TLS session ticket lifetime hint: 86000 (seconds)
    TLS session ticket:
    0000 - 9d da 29 68 b3 d6 c9 eb-89 00 15 71 ff 74 eb cb   ..)h.......q.t..
    0010 - 0a ed 63 c3 6c 6d 55 9c-00 f0 3a 7d 36 1a 60 ee   ..c.lmU...:}6.`.
    0020 - 02 24 ef d1 1c 9c bb 12-7f 40 a8 0e 4b 1c 2d c6   .$.......@..K.-.
    0030 - 35 f9 6a 37 17 a6 f0 65-4f 06 8c 09 f0 a6 4b a6   5.j7...eO.....K.
    0040 - 96 89 93 ff b5 c6 46 f4-fa 80 08 e2 49 22 99 b6   ......F.....I"..
    0050 - 81 50 e2 4f 87 4a 95 aa-f2 4c 25 6e ad 27 ef c0   .P.O.J...L%n.'..

    ... snip ...

^C
```

# TLSに対する攻撃手法

ここでは、TLSに対して知られている攻撃手法について解説している。

## POODLE Attack

> 9月後半にGoogleのチームはSSL 3.0にの[重大な脆弱性](https://www.openssl.org/~bodo/ssl-poodle.pdf)を発見した、それはcookieのようないくつかの機密情報を盗むために利用することが出来る。この脆弱性は"POODLE"として知られ、BEAST攻撃に似ている。この脆弱性を使用することで、攻撃者はパスワードやcookieのようなものにアクセスできるようになり、Webサイト上のユーザのプライベートなアカウントデータにアクセスすることが可能になる。
>
> --- [The POODLE Attack and the End of SSL 3.0](https://blog.mozilla.org/security/2014/10/14/the-poodle-attack-and-the-end-of-ssl-3-0/)

POODLE AttackはSSLv3の脆弱性であり、これを利用することでCookieやパスワードといった情報を取得することが出来る。これを防ぐにはSSLv3を無効にし、TLSを使用する必要がある。

SSLv3しか使用できないクライアントにはWindowsXP Internet Explorer 6、Java 6で実行されるクライアントがある。

## BEAST CVE-2011-3389

> BEASTはAES、Camellia、そしてCBCモードを使っているいくつかの他のプロトコルにおけるCBCモードのInitialization Vector (IV)の脆弱性である。
>
> この攻撃では、MITM(Man in The Middle)攻撃者は、同じメッセージを複数回暗号化されることで、平文の値を復元できる。
>
> BEASTはTLS1.1以上で回避されている。
>
> 詳細: https://blog.torproject.org/blog/tor-and-beast-ssl-attack

## LUCKY13

> Lucky13はCBCモードに対するもうひとつの攻撃で、暗号文を解読するためのパディングチェックを行うことで復号する攻撃である。
>
> 詳細: https://www.imperialviolet.org/2013/02/04/luckythirteen.html

LUCKY13攻撃については次の記事が詳しい。

- [Lucky Thirteen Attackの攻撃手法を説明してみるよ - dec9ue's diary](http://dec9ue.hatenablog.com/entry/2013/03/24/134055)

## RC4 weaknesses

> RC4は、暗号化ストリームの最初の256バイトにおいて、暗号文を解読しやすいことが証明されている。同じデータが何度も暗号化されていた場合、攻撃者は統計的な解析を適用し、暗号文を解読することができる。
>
> 公開されている議論 ([bug 927045](https://bugzilla.mozilla.org/show_bug.cgi?id=927045))において、RC4を3DESで置き換えることが推奨されている。これはInternet Explorer 7/8のユーザにインパクトを与えた。これらのブラウザは、OSに依存しており、AESをサポートしておらず、RC4、または3DES暗号のみでやりとりできる。Internet Explorerは暗号化ライブラリ"schannel"を使用しているが、これはOSに依存している。schannelはWindows VistaにおいてAESをサポートしているが、Window XPではサポートしていない。
>
> 3DESはより抵抗力がある暗号方法だが、RC4に対して、30倍も遅く、よりCPUを必要とする。大規模なWebインフラでは、RC4を3DESで置き換えるコストはゼロではない。このような理由から、管理者はトラフィックパターンを評価して、ケース別にRC4と3DESを置き換えるかどうか決定することを推奨する。
>
> 参考までに、RC4を3DESで置き換える暗号化スイートは次の通りである:
> `ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK`

## CRIME CVE-2012-4929

> この問題の原因は、データが暗号化前に圧縮された時に発生する情報のリークである。もし、センシティブな情報と比較的予測可能なデータが、認知のコンテンツに注入され、混ざっていた時、暗号化されたストリームを観測できるならば、攻撃者はそれから未知のデータを抽出することができる。
>
> 詳細: https://community.qualys.com/blogs/securitylabs/2012/09/14/crime-information-leakage-attack-against-ssltls

## BREACH

> これはCRIMEに比べ、より複雑な攻撃であり、TLSレベルの圧縮を必要としない(HTTP-levelの圧縮は必要とする)。
>
> この攻撃が成功するには、次の用件が必要である:
>
> - HTTPレベルの圧縮を使用している
> - ユーザのインプットがHTTPレスポンスボディに含まれる
> - 秘密情報(例えばCSRFトークン)がHTTPレスポンスボディに含まれる
>
> 詳細: http://breachattack.com/

# まとめ

本記事ではMozillaWikiの記事の抄訳に基づき、HTTPSサーバの各設定について説明した。HTTPSは様々な要素がある複雑な仕組みであり、自分も十分に理解しているとはいえない。ここまちがってるよーとか、こうするといいよーといった意見があれば、コメント、編集リクエスト等で教えてほしい。


## 更新履歴

- 2014/10/15 POODLE Attackに対応するように内容を更新
- 2014/05/20 初稿

## 参考文献


- [Module ngx_http_ssl_module](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_ticket_key)
- [Module ngx_http_spdy_module](http://nginx.org/en/docs/http/ngx_http_spdy_module.html)
- [httpsだからというだけで安全？調べたら怖くなってきたSSLの話!？](http://qiita.com/kuni-nakaji/items/5118b23bf2ea44fed96e)
- [Testing OCSP Stapling | UNMITIGATED RISK](http://unmitigatedrisk.com/?p=100)

## 原典

- 原典: [Security/Server Side TLS - MozillaWiki
](https://wiki.mozilla.org/Security/Server_Side_TLS)

この記事はMozillaWiki記事の抄訳に基づいており、MozillaWikiのライセンスに従い、[Creative Commons Attribution-ShareAlike license (CC-BY-SA) version 3.0](http://creativecommons.org/licenses/by-sa/3.0/)でライセンスされる。