読者です 読者をやめる 読者になる 読者になる

sonickun.log

備忘録

Let's EncryptでHTTPSサーバを建てたついでにSSL LabsでA+評価をめざす

最近 Let's Encrypt が Public Beta になったということで,自分のサイト(https://sonickun.xyz)もSSL化してみた.また,どうせならSSL LabsのテストでA+を取りたいと思いあれこれ試行錯誤したので備忘録として残しておく.

Let's Encrypt

letsencrypt.org
Let's Encrypt は,SSL/TLSサーバ証明書の取得・管理を簡略化できる無料のサービスであり,TLSHTTPSを普及させることを目的としている.Let's Encryptで取得可能なSSL/TLSサーバ証明書は「ドメイン認証 (DV) SSL/TLS証明書」であり,独自ドメインの所有者であれば誰でも取得可能である.企業認証(OV)SSL/TLS証明書やEV SSL証明書は取得できないが,個人が運営するサイト程度ならDV証明書で十分といえる.

Let's Encryptの詳細は有志による日本語サイトによくまとまっている.

Qualys SSL Labs

www.ssllabs.com

Qualys SSL Labs社が提供するSSL Server Testは,SSLサーバ証明書の設定状況の確認や,信頼性の診断などができるサービスである.グレード表記や項目別スコア表示があり,さらに信頼性に乏しいと思われる設定項目を指摘してくれる.
今回はQualys SSL Labsのテストの最高グレードである A+ の取得を目指す.

Let's EncrptでWebサイトをSSL

今回SSL化するWebサイトは自分のプロフィールサイト(sonickun.xyz)で,WebサーバはNginx,OSはCentOS 6.5である.
詳しくは過去記事
sonickun.hatenablog.com

Lets's Encrypt Clientのインストール

事前に以下のパッケージをインストールしておく.

# yum -y install httpd openssl mod_ssl
# yum -y install git

次に以下のコマンドでLets's Encrypt Clientをインストールする.--debugオプションは必須ではないが,これがないとエラーが起きる場合があるので付けておいたほうが良い.

# git clone https://github.com/letsencrypt/letsencrypt
# cd letsencrypt
# ./letsencrypt-auto --help --debug

自分の環境の場合,letsencrypt-autoの実行時にvirtualenvというPythonモジュールがないとがないと怒られてしまったのでeasy_installで入れておく.

# easy_install virtualenv

SSL証明書の取得

以下のコマンドを実行する.${DOMAIN}の部分は取得したいドメイン(自分の場合は"sonickun.xyz")を,${WEBROOT}の部分はNginxで設定しているルートディレクトリ(自分の場合は"/var/www/html/")を指定する.

# ./letsencrypt-auto certonly --webroot -d ${DOMAIN} --webroot-path ${WEBROOT} --debug

すると,青い画面でメールアドレスの入力を求められるので,メールアドレスを入力して,<了解>を選択する.つぎに,利用規約に同意するか聞かれるので,<Agree>を選択する.
証明書の取得が完了すると以下の様な表示になる.

IMPORTANT NOTES:
 - If you lose your account credentials, you can recover through
   e-mails sent to xsonickun@gmail.com.
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/sonickun.xyz/fullchain.pem. Your cert will
   expire on 2016-03-13. To obtain a new version of the certificate in
  the future, simply run Let's Encrypt again.
 - Your account credentials have been saved in your Let's Encrypt
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
  also contain certificates and private keys obtained by Let's
   Encrypt so making regular backups of this folder is ideal.
 - If like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

/etc/letsencrypt/live/sonickun.xyz/に以下のファイルが置かれる.

# cd /etc/letsencrypt/live/sonickun.xyz/
# ls
cert.pem       #サーバ証明書
chain.pem      #中間証明書
fullchain.pem  #サーバ証明書+中間証明書
privkey.pem    #サーバ秘密鍵

Nginxの設定

今回はSSL化に加えて,HTTP/2にも対応させることにする.HTTP/2はHTTP/1.1と比べ様々な最適化がされており,例えば,バイナリーフレームの採用,ストリームによる多重化,サーバープッシュなどの特徴がある.
Nginxでは,mainlineで提供されているバージョン1.9.5以降でHTTP/2に対応しているため,これよりも古いバージョンの場合はアップデートする必要がある.
まず,"nginx: Linux packages"を参考にしつつリポジトリを以下のように変更する(baseurlに注意).

# vi /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/OS/OSRELEASE/$basearch/
gpgcheck=0
enabled=1

yumでアップデート,バージョンの確認をする.

# yum -y install nginx
# nginx -v
nginx version: nginx/1.9.9

これにてNginxのバージョンアップが完了した.

次に,Nginxの設定ファイルetc/nginx/conf.d/server.confを以下のように設定する(なおこれが最終版ではない).

server {
    listen 80;
    listen [::]:80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name  sonickun.xyz;

    ssl_certificate /etc/letsencrypt/live/sonickun.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sonickun.xyz/privkey.pem;
    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:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256: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_dhparam /usr/local/nginx/conf/dhparam.pem;

    location / {
        root    /var/www/html;
        index  index.html index.htm;
    }
}

設定変更後のNginxの再起動も忘れずに.

# service nginx reload
# service nginx restart

Webサイトの確認

実際にアクセスしてみるとHTTPSで通信していることが分かる.また,Googleの拡張であるHTTP/2 and SPDY indicatorを使ってHTTP/2が使われていることが確認できる.
f:id:sonickun:20151218221148p:plain

chrome://net-internals/#events&q=type:HTTP2_SESSION%20is:active
f:id:sonickun:20151218221630p:plain


SSL Labs A+ を目指して

ひとまず先程のSSL Labsのテストを行ってみる.
f:id:sonickun:20151218222057p:plain

はい.

診断結果によれば,DH鍵が弱く,Perfect Forward Secrecy (PFS) に対応していないとのこと.

DH鍵の変更

DHE鍵交換にはLogjam攻撃という攻撃が存在し,512bit長の標準素数であれば数十秒で解ける.また1024bit長でも国家予算並みのお金をかければ解読できると言われており,現在は2048bitの利用が推奨されている.
さて,現状のDH鍵の鍵長は1024bitとなっているため,2048bitのものに変更する.
まず/usr/local/nginx/conf/下でopensslを用いてDH鍵を生成する.

# openssl dhparam -out dhparam.pem 2048
# openssl dhparam -text -in dhparam.pem -noout

次にserver.confに以下の行を追加する.

ssl_dhparam /usr/local/nginx/conf/dhparam.pem;

これにてDH鍵の変更は完了.

Perfect Forward Secrecy (PFS) ヘの対応

Perfect Forward Secrecyとは,サーバの秘密鍵が暴露された場合でも,ユーザーの個人的な情報記録を過去に遡って解読することを妨ぐ暗号化技術であり,HeartBleedのような致命的なバグに対して効果を発揮する.
PFSをサポートしているのはDHE鍵交換とECDHE鍵交換である.
したがって,server.confssl_ciphersの部分を以下のように変更する.

ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

Chromeのアドレスバーの鍵マークをクリックすると,ECDHE鍵交換が行われているのが確認できる.
f:id:sonickun:20151218230200p:plain

HTTP Strict Transport Security (HSTS) ヘの対応

HSTSでは,サーバから"Strict-Transport-Security"というHTTPヘッダを送ることで,クライアントに常にHTTPSで通信するように強制させることができる.この指定はmax-ageに指定した期間クライアントにキャッシュされる.これにより,中間者攻撃より知らず知らずのうちに攻撃者に平文通信を盗聴されることを防ぐことができる.
SSL LabsのテストではHSTSが有効になっているとスコアが上がるとの事だったのでこれを有効にすることにする.Nginxではserver.confに以下の行を追加することでHSTSが有効になる.

add_header Strict-Transport-Security "max-age=15768000; includeSubdomains";

実際にHTTPSで接続した後,URLの"https"を"http"に変更してアクセスすると,すぐさまHTTPSのサイトにリダイレクトする.レスポンスヘッダを見ると"Strict-Transport-Security"ヘッダが付加されていることが確認できる.
f:id:sonickun:20151218232056p:plain

再テスト

再びSSL Labsで診断した結果,めでたくA+を取得できた:)
f:id:sonickun:20151218232644p:plain

最終的な設定ファイルserver.confは以下のとおり.

server {
    #listen 80;
    #listen [::]:80;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name  sonickun.xyz;

    ssl_session_timeout  10m;

    ssl_certificate /etc/letsencrypt/live/sonickun.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sonickun.xyz/privkey.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

    ssl_prefer_server_ciphers on;
    ssl_dhparam /usr/local/nginx/conf/dhparam.pem;

    add_header Strict-Transport-Security "max-age=15768000; includeSubdomains";

    location / {
        root    /var/www/html;
        index  index.html index.htm;
    }
}

おわりに

Let's EncrptによるSSL証明書の取得は思ったよりも簡単に行うことができた.また,HTTPSにしたからといって安全と高をくくってはいけないと学んだ.

MozillaGoogleをはじめとする多くの団体や企業があらゆるWebコンテンツをHTTPSに移行するように呼びかけており,SSL化されたWebサイトはどんどん増えている.GoogleではHTTPS ページが優先的にインデックスに登録されるようになる.

一方で,未だにHTTPSに対応していない大手のWebサービスも多々あり,それらのHTTPのコンテンツがHTTPSのWebサイト上で動作しないといった問題が起きている.

もちろんセキュリティ上の問題もあるが,この度のLet's Encryptのリリースによってこれらの問題が改善されていくことが期待される.