なうびるどいんぐ

脳みそ常時-3dB

SSL評価サイトでA+を取る設定

      2017/05/21    HimaJyun

もうお気づきの方も居ると思われますが、このブログ、Let's Encrypt製証明書でECDSAを使用し暗号化、HTTP/2に対応しています、先進的でしょ?

と言う訳でド定番ネタですが、そのSSL設定、SSL Server Testで評価をA+にするための方法をいくつか紹介しましょう。

スポンサーリンク

最初に

一応言っておきますけど「A+を取ったから安全」と言う訳ではありませんよ。

そもそもWebサイト側にSQLインジェクションとかあったのでは無意味です。

Webサイトを安全にするのは安全にしようと言う意志(を持つ人の手)です。

「SEO評価ガー」でHTTPSにしておいて脆弱性放置なんてもってのほかですぜ。

鍵の発行

この記事では鍵の発行に関しては取り扱っていないので、過去記事「[スクリプト有]Let's EncryptにECDSA(ECC)の鍵を作成してもらう」でもご覧になってください。

今回はECDSA大前提で書いているので、RSAとかだとCipherSuiteが変わって来るかもです。

設定していく

今、画面の前に居るあなたが最低限HTTPSでの接続設定は出来るレベルだと仮定しましょう。

Mozilla SSL Configuration Generator」なんかがとても役に立ちます(一ヵ所変えた方が良い設定がありますけど)

古いプロトコルを無効にする

SSLv3やSSLv2などは既に脆弱です、一般的に「SSL」と言われている物は全て「TLS」の事です。

nginxであれば以下の様に、TLSv1,TLSv2,TLSv3を指定します。

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ApacheであればallからSSLv3を抜くのが良いでしょう。(今時のApacheではSSLv2などは既にallには含まれていない)

SSLProtocol all -SSLv3

適切な暗号スイートの選択

先程のMozzilaのジェネレータで作成した設定ではとてもとてもとてもとても長い暗号化スイートが出力されます。

が、ここはHIGHを指定し、不要(危険)な暗号化スイートを抜いて行くのが個人的にはおススメです。

# nginx
ssl_ciphers 'AESGCM:HIGH:!aNULL:!eNull:!EXPORT:!DES:!3DES:!MD5:!DSS:!RC4:!PSK';
# Apache
SSLCipherSuite AESGCM:HIGH:!aNULL:!eNull:!EXPORT:!DES:!3DES:!MD5:!DSS:!RC4:!PSK

HIGHは暗号に128bit以上の鍵を使う物を指定する設定で、例えばOpenSSLが新しくなり、使えるスイートが増えた、なんて時に自動で新しい物も使える様になります。

ちなみに「AESGCM:HIGH」としているのは、こうしないとHTTP/2有効時にFireFoxから閲覧するとHTTP/2の前提条件と矛盾し繋がらなくなるため。

ここの設定で繋がる/繋がらないクライアントがだいたい決まります、今回の設定ではXPなどのサポート切れOSを切り捨てています、今だとVistaすら切り捨てられるはずなので更にセキュアに出来ると思われ

暗号化スイートをサーバ側に選択させる

デフォルトだと暗号化スイートなどをクライアント側が選択するようになっています。

この際、悪意を持った第三者が通信をごにょごにょする事で意図的に弱い暗号化を使用させる事が出来るらしいです(ダウングレード攻撃)

そこで、使用する暗号化スイートをサーバ側で選択するようにしてしまいます。

# nginx
ssl_prefer_server_ciphers on;
# Apache
SSLHonorCipherOrder on

ダウングレード攻撃周りはちょっと難しいのですが、とにかくサーバ側で選択するようにすれば良いのです。

HSTSを使用する

「普通は」常時SSLのサイトに対してHTTPでアクセスするとHTTPSへリダイレクトします。

しかし、HTTPの通信は平文で行われるため、ここで攻撃者側がごにょごにょする事が可能になってしまいます。

そこで、一度HTTPSのサイトにアクセスすると「次以降は必ずHTTPSで来てね」と言う事をブラウザに覚えておいてもらうHSTS(HTTP Strict Transport Security)と言う物があります。

と言っても、設定は簡単で、「Strict-Transport-Security」ヘッダを送るだけ。

# nginx
add_header Strict-Transport-Security 'max-age=31536000;';
# Apache(mod_headersが必要)
Header always set Strict-Transport-Security "max-age=31536000;"

max-ageで指定した秒数が有効になる時間です。

すなわち、今回の場合、そのユーザのアクセスから最長1年間(31536000秒)の間HSTSを有効にします。

これの設定には注意が必要で、「やはりHTTPに戻したい」と言う状況になった時、このHSTSが有効な間にアクセスして来たユーザは強制的にHTTPSで接続しようとし、結果的に接続出来なくなります。(すなわち、一度設定してしまうと未来永劫HTTPSであり続ける事を約束した様な物)

なので、もしHTTPに戻すような事があれば(あるのであれば)半分の15552000秒(半年)や、2592000秒(1ヶ月)だけにしておく必要があるでしょう。(それでも1ヶ月間はアクセス出来ないかもしれないユーザが発生する事になる訳なのですが……)

あと、コピペ設定で「includeSubDomains;」が入っている設定例を見掛けますが、これは文字通りの意味で、設定するとサブドメインまでHSTSの範囲を広げます。

簡単に説明すると、example.jpでincludeSubDomainsが入ったHSTSを受信すると、HTTPにしか対応していないplain.example.jpにアクセスしようとした場合にもHTTPSで接続を試みる->結果として接続出来ない、と言うザマになります。

良く分からないなら設定しない方が良い諸刃の剣です。

DH鍵の変更

DH鍵交換の際に使用する素数は2048bit以上の物を使用する事が推奨されています。(Logjam攻撃)

そのため、OpenSSLで新しい鍵を作成し、そちらを使用するようにします。

sudo openssl dhparam 2048 -out /path/to/dhparam.pem

以下、nginxの設定

ssl_dhparam /path/to/dhparam.pem;

Apacheはどうやってやるんだろ……?

高速化

「HTTPSを利用すると遅くなる」なんて言われますが、それらは上手く調整する事で十分カバー出来ます。

と言うか、「HTTPSで重くなる」なんていっているその時間で転送量を減らす工夫でもすべきだとは思うのですが……

SSLセッションキャッシュの利用

SSLは初回接続時のセッションの確立が特に時間が掛かります。

と言う訳で一度確立したセッションを使い回しちゃおうぜ、ってのがSSLセッションキャッシュ。

# nginx
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
# Apache
SSLSessionCache shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)
SSLSessionCacheTimeout 86400

SSLセッションキャッシュが利用されているかどうかはrfc5077とか言うツールで確認できます。

Ubuntuでインストールする場合は以下の通り

sudo apt-get install -y pkg-config libssl-dev libev-dev libpcap-dev libgnutls-dev libnss3-dev
git clone https://github.com/vincentbernat/rfc5077.git
cd rfc5077/
git submodule init
git submodule update
make

これでカレントディレクトリに「rfc5077-client」が出来上がる。

使い方はこんな感じ

./rfc5077-client -s jyn.jp {IPアドレス}

SSLセッションチケットは使わない

先程のSSLセッションキャッシュはセッションを識別する番号をクライアント側に送り付けるやり方です。(セッション情報がサーバ側に保存されている)

SSLセッションチケットはその逆で、セッション情報を暗号化しクライアント側に保存させるやり方です。

なのだが……Apacheやnginxは鍵を起動時に作成する(定期的に再生産されない)ため、前方秘匿性が満たせないらしい。

知らずにonにしていたが、offの方が安全

そのことを理解したうえでonにしたい場合は以下の通り。

# nginx
ssl_session_tickets on;
# Apache
SSLSessionTickets on

複数サーバ間でセッション情報を共有する必要がある場合は、チケット用の48バイトの乱数を作成します。(その必要がなければ無指定=起動時にランダム生産の方がセキュリティ的に強いかと思われ)

openssl rand 48 > /path/to/ticket.key

で、後はちょちょいと設定して、と……

# nginx
ssl_session_ticket_key /path/to/ticket.key;
# Apache
SSLSessionTicketKeyFile /path/to/ticket.key

OCSP Staplingの使用

証明書の有効/無効を確認するためにOCSPで確認するのですが、これはデフォルトだと外部に問い合わせするようになり、遅延や接続出来なかったりが多々あるみたいです。

そのため、このOCSPの情報を接続時に一緒に送ってしまおう、と言うのがOCSP Staplingです。

設定は以下の通り

# nginx
ssl_stapling on;
ssl_stapling_verify on;
# resolverはルータのDNSでも良し
resolver 8.8.8.8 valid=300s;
resolver_timeout 5s;

# Apache
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:${APACHE_RUN_DIR}/ocsp(128000)

HTTP/2を利用する

せっかく暗号化したのですからHTTP/2を使える様にしましょ、HTTPSなのにHTTP/2使っていないなんてHTTPSのメリットを6割くらい損しているようなものです。(規格では一応平文HTTP/2もあるんですけどね)

あと、SPDYはもう古い。

nginxは(対応しているなら)listenにhttp2を追加するだけ

listen 443 ssl http2;
listen [::]:443 ssl http2;

http2 ≒ sslなのでsslがなくてもSSLになる(と思う)のですが、一応sslを付けておきましょう。

Apacheはmod_http2のインストールが必要(とは言え手元で動いてるApcaheはバージョン不足なので未確認)

ProtocolsHonorOrder On
Protocols h2 http/1.1
# ちなみにh2cなら平文HTTP/2だけどまずブラウザが対応していない
#Protocols h2c http/1.1

最後に

私はHTTP/2が利用したくて常時SSL化した訳なのですが、せっかくなので評価もA+にしておくと良いですね。

GoogleもSSLの有無をSEO評価に反映するのは結構ですが、この辺りのチェックも入れてくれればより良いかな?

そうすればいい加減なSEO屋が全滅して、しっかり設定している人達が報われます。(個人的にA以上の設定が出ていないSSLは無意味だと考えていたり)

 - サーバー