SSLを導入しよう

# はじめに
近年、HTTPSなサイトが増えてきました。これは、サーバーとクライアントの通信を暗号化して行い、第三者の傍受や改ざんを防ぐのが目的です。このため、古くから決済処理などで使われていました。

ただ、全体がHTTPSというサイトはあまりありませんでした。というのは、暗号化や復号化で通信にオーバーヘッドが発生するためです。実際決済サイトの表示だけ重いということはよくありました。

近年パソコンやサーバーの性能も上がりあまり問題にならなくなってきたのと、IFRAMEを悪用したクリックジャッキングなどの問題が増えてきたことや、Googleが前述の理由からHTTPSサイトをサーチエンジンのクロールで優遇すると発表したことにより、徐々に普及しつつあります。

しかし、SSLというと高価なイメージがあります。例えば、大手のGeoTrustの場合年間31,300円、GlobalSiginの場合年間59,800円です。一般的なレンタルサーバーの中位プランよりも高いですね。SSLとはサーバーの身元保証も兼ねているのでそれだけ大事ということです。

身元保証するといえば、当然その保証人も身元が保証できなければ意味が無いのでクロス証明と呼ばれる認証局同士の証明というものもあり、当然そのコストも加算され安心料としてどうしても高くなってしまいます。

このため、この認証局がうっかりミスをしてしまうと大問題になります。以前は、無料SSLといえばStartSSLでしたが、<a href="https://security.srad.jp/story/16/09/02/1040209/">親会社のWoSignがやらかした</a>ため配下のStartSSLまで被害を被ることになり、たとえ有料プランでお金を払っていてもブラウザからは「不正な証明書です」という扱いになってしまいました。結局StartSSLは今年を最後に認証局をやめるみたいです。

最王手のVeriSignで有名なSymantecでも、証明の透明性が担保されてないということでChromeを開発しているGoogleが問題視し、一部の証明書を「信頼できない証明書」としました。この結果サイトによって、ブラウザのコンソールに「The SSL certificate used to load resources from https://なんちゃら will be distrusted in M70. Once distrusted, users will be prevented from loading these resources. See https://g.co/chrome/symantecpkicerts for more information.」という表示が出るサイトが今年の上半期によくありましたが、まさにこれが原因です。

SSLを導入する最も大きな目的は、決済処理などで外部からクレジットカードの番号などが第三者に知られないようにするためです。また、サーバー証明書という言葉があるように、例えばこのサイトがqiitaのサイトであるということを証明するという目的にも使われます。

過去に、不正なアドウェアが混入して、例えばAmazonや楽天のショッピングサイトで買い物をする時、実際は全然別のサイトで決済をしてしまうことがありました。俗に言うフィッシングです。サイトのデザインが似ているだけでドメインやアドレスを見ずに、本物のサイトだと思ってしまう人がただでさえ多いのに、アドレスバーまで偽装されていたら大変なことになります。

他にも、これは、サイト管理の問題というよりも利用者の問題ですが、公衆Wifiやプロキシサーバー経由でネットにアクセスすると、悪意を持ったアクセスポイントやサーバーならばトラフィック内容から通信内容を傍受できてしまいます。Cookieなんかもダダ漏れになるのでIDやパスワードを抜くのは容易です。

そんな環境でショッピングサイトを使うな！と思うかもしれませんが、これもSSL化で対処できます。最初に述べたようにサーバーと利用者の通信内容をお互いのみがわかる状態にするのでたとえ悪意を持ったアクセスポイントを使っても通信を解析するのは困難です。（とはいえ0ではないので信頼できないネットワークで決済などをするのは避けましょう）

「IDパスワードやクレジットカードの番号や銀行口座のやり取りが無い普通のサイトだし、別になくてもへーきへーき。というか暗号化する分遅くなるじゃん」

いえいえ、そんなことはありません。今ではむしろ、SSL化によってサイトの表示が早くなります。正確にはHTTP/2に対応したサーバーが必要ですが、最近のHTTPDサーバーとして広く使われているApacheやnginxはすでに対応済みで、ちょっとした設定変更で有効化できます。

# HTTP/2って何？

HTTP/2では10年以上使われてきたHTTP/1.1では、TCPの仕様上、1度に1個の情報しかやり取りできません。これを束ねて何度も同じサーバーと通信を繰り返すので当然非効率ですしサーバーに余計な負荷がかかります。わかり易い例で言うと、サイトを表示するときに、HTMLを送りました、CSSを送りました、画像を送りました、JSを送りましたみたいな感じで各々が別のリクエストとして処理されています。この例では、ページが表示されるまでに４回リクエストがあるということになります。

こういった問題を避けるため、昔のファミコンのソフトのように単一画像からCSSで一部分を抜き出してアイコンなどのパーツにしたり（CSSスプライト）、AMPでは外部スタイルシートを禁止してページ内に埋め込んだり、JSやCSSを圧縮結合するツールなどを入れてリクエスト回数を減らす試みをしてきました。

しかし、HTTP/2ではこれをまとめて送信する機能が備わっているためページの表示は早くなります。重要なのは **HTTPSの通信の暗号化コストよりも通信方法の最適化が上回っている** というところです。常時SSL化は高速化にメリットがあります。どれくらい違うかは以下のサイバートラストのデモサイトで確認できます。

<dl>
<dt>通常時</dt>
<dd><a href="http://http2vs1.cybertrust.ne.jp/">http://http2vs1.cybertrust.ne.jp/</a></dd>
<dt>HTTP2</dt>
<dd><a href="https://http2vs1.cybertrust.ne.jp/">https://http2vs1.cybertrust.ne.jp/</a></dd>
</dl>

自分の環境では、通常時３秒半かかっていたのが、HTTP2では半分以下の１秒半程度になっていました。

# SSL化のデメリット
SSL化にはもちろんデメリットもあります。

## SSL化によってページの速度遅くなるんじゃないか？

SSLは認証局という第三者によってその鯖の所在を確認した後に、サーバーとクライアント間のみが復号化できる暗号化通信をするため、SSL認証局にアクセスるるための時間、暗号化、復号化にかかる処理時間や負荷などで、どうしても直接アクセスするよりオーバーヘッドが生じてしまうということです。

前回も書きましたが、これはインターネット環境が貧弱だったりサーバーやクライアントの性能が低かった時代の話で、今は誤差の範囲でしかありません。気にしなくてもいいでしょう。

## 古いブラウザでアクセスできなくなる

これは2014年に起きたPoodle騒動が発端になっています。ひとことにSSLといっても色々バージョンがあり、これはその時点ですでに時代遅れになっていたSSL3.0に脆弱性が見つかったため、TwitterなどのサービスでSSL3.0でアクセスできなくなるような締め出しが行われた結果、閲覧できなくなったというのが真相です。

もともと、これの対象になるようなブラウザはIE6だったり古いガラケーだったりと、すでにサポート切れで使う方に問題があるようなものばかりです。（旧式のルーズな実装の穴を突いて悪さをするというパターンですね）

## SSLは高価だ

グローバルサインやサイバートラストのような大手だと月数万と当然高いです。その一方でLet's EncryptやCloudFlareでは無料でSSLが使えます。これらの違いは、サーバーの身元保証にあります。同じSSLでも身元保証する重要性が高いサイトではこのスクリーンショットのようにアドレスの左に団体名が入ります。（EV証明書。これはサイバートラストの例）

![cybertrust.png](https://qiita-image-store.s3.amazonaws.com/0/15020/5c8590bc-0f01-3332-35cb-5db18059a2b3.png)


多くのサイトではそこまで必要ではないでしょう。個人サイトやコーポレートサイト、商品紹介サイト、閲覧のみのデーターベースサイトでは、Let's Encryptの無料SSLでも十分だと思います。

しかし、個人情報を扱ったり決済処理があるサイトで無料SSLというのは、保証という面からも問題があるので素直に有料SSLを使いましょう。前述の団体名が入るタイプのEV証明書だとなおいいです。

このように、目的によって使い分けることは大事です。

## 設定がめんどい

サーバー証明書、プライベートキー、チェーン証明書など専門用語が多く設定も煩雑です。しかし、最近ではACMEという規格があり、ほぼ発行処理や更新の自動化ができるようになり、だいぶ敷居が下がっています。

NASの中には自動発行、自動更新するものもある時代なので今後敷居は下がっていくものと思われます。

## アクセス解析が機能しない

マーケティングにおいておそらくリスクになるのはここではないでしょうか。GoogleがSSL強制化を行った際に、アクセス解析で検索キーワードの取得ができなくなりました。SSL経由の場合リファラーを吐かないためです。この場合、どちらのサイトもSSL化するか、リンク元のサイトのヘッダーにmeta name=”referrer”を追加する必要があります。

リファラーなどに価値があるサイトでは慎重にしたほうがいいかもしれません。

# Let's Encryptを使ってみよう

Let's Encryptという団体は、世界中のWebサイトを全てSSLにしようという趣旨の2014年に発足し2016年より正式サービスを開始しした団体です。ここでは無料でSSLを発行できます。Let's Encryptの身元を保証しているルート証明書は、アメリカの<a href="https://www.identrust.com/">IdenTrust</a>のものが使われており、これは世界のSSLのシェアの1割を占めます。

SSLの設定は非常にややこしいです。まず認証局と契約をして、サーバー上でコマンドを打ってフィンガープリントを作って、これをSSL認証局に提出してキーを取得受け取った公開キーをHTTPDサーバーなどに設置します。お世辞にも万人向けではありませんね。ところが、Let's Encryptではたった２つのコマンドで発行できるようになっています。

注意しなければならないのはLet's Encryptで発行しているSSLの有効期限は90日と短く、その都度SSLを更新しなければならないところです。あと、発行しているSSLはあくまでもドメインの証明（DV）だけで企業名・団体名（OV）の保証やより厳密な保証（EV）はサポートしていないところです。平たく行ってしまうと、SSL通信するときにブラウザに警告がでないようにするためのSSLって割り切って考えたほうがいいですね。

SSLの更新は面倒だと言いましたが、Let's Encryptではこの作業を<abbr title="Automatic Certificate Management Environment">ACME</abbr>という独自のプロトコルを利用して実装しcertbotを実行するだけで自動更新てきます。また、大抵のLinuxのディストリビュージョンにはこのコマンドが標準でリポジトリに含まれておりインストールさえすればすぐに使える状態です。

## acme.shを使った設定

ここでは、<abbr title="Automated Certificate Management Environment">ACME</abbr>を使ってSSLを自動的に発行する作業を行う方法を書きます。ここでは、acme.shを使って説明します。

まず、<a href="https://acme.sh">acme.sh</a>をインストールします。とはいっても、このサイトに記載されている通りにやればいいだけです。

ルート権限になり、
<pre>
curl https://get.acme.sh | sh
cd .acme.sh/
acme.sh --issue --dns -d [ドメイン名]
</pre>
と打ち込みます。すると、以下のようなメッセージが帰ってきます。

<pre>
Add the following txt record:
Domain:_acme-challenge.[ドメイン名]
Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
</pre>
この_acme-challengeというサブドメインのテキストレコードにパラメータを入れることでそのドメインの所有者であることの証明を取ります。

実際にDNSに登録する際は以下のように入力します。
<pre>
txt _acme-challenge 9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
</pre>
DSNの反映には時間がかかるかもしれませんので慌てずに。

次に実際にSSL証明書の発行を行います。
<pre>
acme.sh --issue -d [ドメイン名]
</pre>
こうすると、[ドメイン名]フォルダがあacme.shのあるディレクトリに設置されます。このディレクトリの中にSSL証明書が設置されます。
<pre>
root@ホスト名:~/.acme.sh/[ドメイン名]# ls
[ドメイン名].cer   [ドメイン名].csr       [ドメイン名].key  fullchain.cer
[ドメイン名].conf  [ドメイン名].csr.conf  ca.cer
</pre>
生成されたファイルは以上になります。このうち必要なのは、[ドメイン名].cerと、[ドメイン名].keyです。例えば、nginxでSSLを使いたい場合、SSLの設定に以下のようにパスを登録します。
<pre>
ssl_certificate      /root/.acme.sh/[ドメイン名]/[ドメイン名].cer;
ssl_certificate_key  /root/.acme.sh/[ドメイン名]/[ドメイン名].key;
</pre>

あとはHTTPDサーバーをリロードするだけです。うまく動いたかどうかはSSLLabで確認できます。
<pre>
https://www.ssllabs.com/ssltest/analyze.html?d=[ドメイン名]&amp;latest
</pre>

鍵交換等の処理をほぼ自動化してやってくれるので簡単ですね。

Let's EncryptはこのようにSSL導入が簡単ですが、反面有効期間が３ヶ月と非常に短いです。昔無料SSLをやってたStart SSLの有効期間の１年を考えると、普通のSSL認証局の試験運用機関かよと言わんばかりの短さです。

これは、無料でだれでも簡単に鍵が発行できるので不正利用リスクが上がるというところにあります。他にもテスト使用のための実際使われない鍵も多いと思われます。このためか、90日おきに更新するようになっています。本来であればSSLの認証作業は更新時も鍵の入れ替えをしなければならないなど面倒ですが、Let's Encryptの場合ACMEを使用するため基本的にcronコマンドを実行するだけで済みます。

コマンドは以下のようになります。
<pre>
0 0 * * * "/root/.acme.sh/acme.sh" --cron --home "/root/.acme.sh" &gt; /dev/null
</pre>

これならば別に有効期間が短くてもいいですね。

なお、今年からLet's Encryptではワイルドカードのサブドメインの証明書の発行にも対応しました。使い方は以下のようにするだけです。サブドメインなしのドメインの場合は別途指定する必要があるため発行要求する証明書は２つになります。
<pre>
acme.sh  --issue -d [ドメイン名]  -d *.[ドメイン名]  --dns dns_cf
</pre>

前述したとおり、SSL化はセキュリティの理由もありますが、サーチエンジン最適化の面からにおいても、HTTP/2対応においても常時SSL化というのは必須の流れです。少なくとも新規サービスを立ち上げる場合は常時SSLにしたほうがいいと思われます。

# CloudFlareを使った無料SSL

無料でSSLが使えるサービスは<a href="https://cloudflare.com">Cloudflare</a>でもやっています。CloudflareのアプローチはLet's Encryptと異なり、DNSをCloudflare側で管理することの付随としてやっている側面が強いです。言うなれば、名前解決時にプロクシサーバーとして機能しているということですね。

![cf-ssl1.png](https://qiita-image-store.s3.amazonaws.com/0/15020/8dc453e9-0bbc-8ff6-7ce4-789b94c0d8e0.png)

つまり、すべてのサーバーへのトラフィックはCloudflareを経由してアクセスされることになります。このときSSLの暗号化をCloudflare側でやることで更にセキュアにできるという発想です。このため、単純にSSLを使いたいだけならば、Cloudflare側のボタン一つでSSL化できます。これが、CloudflareのFlexible SSLというサービスです。

このFlexible SSLはCloudflareとサーバー間のトラフィックはそのままですが、CloudflareとインターネットのトラフィックをすべてSSLするという動作になっています。手軽にSSLが導入できる半面HTTP/2などのメリットを活かすことができません。

もうひとつのSSL (Strict)というものは、通常のSSLのようにサーバー側に証明書をいれる必要があります。この証明書は以下のパネルのCreate Certificateボタンから取得できます。

![cf-ssl2.png](https://qiita-image-store.s3.amazonaws.com/0/15020/41a16378-2c55-62e8-eac0-fa15cf0eef91.png)

すると、この画面が表示されます。List Hostnameに登録したいドメインを入れていきます。ワイルドカードも使えます。SSLの有効期限はデフォルトの15年でいいでしょう。

![cf-ssl3.png](https://qiita-image-store.s3.amazonaws.com/0/15020/a7d0c4e1-d692-6f00-cc7f-418e3ab8e9a9.png)

するとこの画面が表示されます。

![cf-ssl4.png](https://qiita-image-store.s3.amazonaws.com/0/15020/3f51381a-076e-c048-4ac4-5933e803133c.png)

上のOrigin Certificateをcertificate.pem、下のPrivate keyをprivate.keyというファイル名にして保存しましょう。最後に画面の一番下にある説明どおりにキーをサーバーのHTTPDサーバーに読み込ませます。

例えば、これらのファイルを/root/cert/に設置するとして、Apacheの場合は
<pre>&lt;VirtualHost *:443&gt;
DocumentRoot [ドキュメントルート]
ServerName [ドメイン名]
SSLEngine on
SSLCertificateFile /root/cert/certificate.pem
SSLCertificateKeyFile /root/cert/private.key
&lt;/VirtualHost&gt;</pre>
というように設置します。

nginxの場合は、各サーバーのディレクティブに以下のように入れます。ssl.confみたいな名前にしてincludeで呼び出してもいいかもしれませんね。
<pre>
listen 443;

ssl on;
ssl_certificate  /root/cert/certificate.pem;
ssl_certificate_key /root/cert/private.key;
</pre>

注意したいのはこの方法だとCloudflareを経由するようにしないとSSLが有効にならない点です。

![cf-ssl5.png](https://qiita-image-store.s3.amazonaws.com/0/15020/101d6335-52f6-47ac-ebc0-8a3f6780b409.png)

ワイルドカードが使えるとはいってもサブドメインごとにDNSを登録し、この図の紫のまるで囲ったようにオレンジ色のCloudflareのアイコンがないドメインはSSLが有効になりません。

cloudflareを使う上での制約やメリットは他にもありますがこれについては別に話します。

うまく動いているとサイトに繋ぐと鍵印が表示されます。ちなみに、Cloudflareの場合は<a href="https://comodo.jp/">COMODO</a>を使っています。

![cf-ssl6.png](https://qiita-image-store.s3.amazonaws.com/0/15020/a70e6ae6-9565-95d1-dc90-b455066ca8d8.png)

# おまけ

SSL証明書はWebminやメールサーバーなどでもキーを読み込ませることで流用できます。今回は割愛しますがそちらでも使うようにすることを推奨します。