This site is a mirror of ama.ne.jp.

つよいSSLサーバを作る

Tornadoでは簡単にSSLサーバを構築できるということで、指定できるオプション等について調べるついでにSSL Server TestでA+を取れるように頑張りました。現在の評価はSSL Server Test: ama.ne.jpで見ることができます。

(2016-11-05) もうこのサイトのバックエンドはTornadoではありません。上記のサーバーテストはA+を返すかもしれませんし、返さないかもしれません。

なお、A+を獲得するには、適切な暗号スイートの指定はもちろんのこと、Strict-Transport-Securityレスポンスヘッダが必要です。

SSLサーバの設定

さて、ama.ne.jpは今のところ以下のようなHTTPServerで動作しています。

server = HTTPServer(app, ssl_options={
    "certfile": options.ssl_crt,
    "keyfile": options.ssl_key,
    "ssl_version": ssl.PROTOCOL_TLSv1_2,
    "ciphers": "EECDH+HIGH+AES!SHA:" +
               "!ADH:!RC4:!MD5:!aNULL:!eNULL:!SSLv2:!LOW:!EXP:!PSK:!SRP:!DSS:!KRB5",
})

ここでは、Tornadoのnetutil.pyを元に、おそらくHTTPServerにssl_optionsとして与えられるであろうと思われるオプションについて書いていきます。

定数やオプションなどの情報はPythonのsslモジュールやopensslのciphersに詳しいです。

ssl_version

使用するSSL/TLSのバージョンを指定します。PROTOCOL_TLSv1_2でTLS1.2のみを使用できる状態になります。

TLS1.0について

TLS1.0にはBEAST攻撃に対する脆弱性があります。ただし、古いブラウザをサポートしなければならない地獄のような状況下なら有効にしなければならないかもしれません。PROTOCOL_SSLv23 | OP_NO_SSLv2 | OP_NO_SSLv3で、TLS1.0、TLS1.1、TLS1.2を使用できる状態になります。

ciphers

oepnsslの記法に倣って使用する暗号スイートを指定します。

暗号スイート指定で用いる記号

  • :は暗号スイート指定の区切りです。
  • プレフィクス+でその暗号スイートを追加します。ただし、1つの暗号スイート指定内で接続されて用いられた場合はAND条件を指します。
  • プレフィクス-でその暗号スイートを取り除きます。ただし、1つの暗号スイート指定内で接続されて用いられた場合はNAND条件を指します。また、これより後ろの暗号スイート指定で再び追加可能です。
  • プレフィクス!でその暗号スイートを使用しません。これより後ろの暗号スイート指定でも追加することはできません。

今回指定した暗号リスト

FS(Forward secrecy)を満たすECDHE-RSAのうち、鍵長の大きなものを指定しました。一応後ろで、弱いものや使わないものを取り除いています。

certfile

自身の公開鍵の格納場所を指定します。CA証明書を含んでいてもかまいません。CA証明書を含む場合は、必ず上から

  1. 自身の証明書
  2. 中間CA証明書
  3. より上位の中間CA証明書 ...

のように、より下位の証明書から順に並べます。ルート証明書は各ブラウザが持っているはずです。

keyfile

自身の秘密鍵の格納場所を指定します。

cert_reqs

証明書の妥当性チェックを行うかを指定します。サーバー側には必要ないので特に指定しません。

ca_certs

(2016-05-29) 証明書の妥当性チェックに使うCA証明書を指定します。サーバー側には必要ないので特に指定しません。

Strict-Transport-Securityヘッダの設定

HTTP Strict Transport Security(HSTS)は、「次からウチのサイトとはHTTPSで通信してくれ」というメッセージを伝える機能です。

幸いTornadoの*Handlerにはset_default_headersメソッドが用意されているので、以下のようなクラスを作ってアプリケーション内の各ハンドラで継承して使うとよいでしょう。

class HstsRequestHandler(RequestHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def set_default_headers(self):
        self._headers.add("Strict-Transport-Security",
                          "max-age=31536000;includeSubdomains;preload")

なお、HSTS preload listに登録するには、長いmax-age(1年=31536000秒くらい?)やincludeSubDomainsおよびpreloadの設定が必要です。

(2016-02-25) ChromeのHSTS preload listにama.ne.jpが入ったようです。

終わりに

自己満足しか得るものがない努力だった。

「読んだ」を押すと、あなたがボタンを押した事実を明示的に通知してこのページに戻ります。このページに戻ってからブラウザの「戻る」ボタンを押すと、何度か同じページが表示されることがあります。