2014年10月29日水曜日

同一サーバ内でひとつのIPアドレス&ポートで複数のSSLを使えるようにする「SNI」についてのまとめ

おことわり


知人からこのエントリで取り上げているネタについて聞かれたので、自分の知識を整理する意味も込めてこのエントリとしてまとめておく。

本エントリにおいては、特段の断りがない限りApache、それも2.4系をベースに記述している。内容の一部については他のWebサーバソフトウェアでも共通して参照できるものがあるかもしれない。

また、本エントリ中にはn個の間違いが含まれている。それらがどこにあるのかを書くには余白が足りないため、読者の宿題としておこう。

バーチャルホストについての基礎知識


「1台のサーバで複数サイトを運用したい……」というのはよくある話。そりゃあ、太古の昔なら事情が違っただろうけども、今のご時世ではそのような構成になっていないことの方が珍しい……といっても言いすぎじゃないはず。

で、そんなときにはバーチャルホスト(VirtualHost)設定を行うことになる。このバーチャルホストは「IPベース」と「名前ベース」の2つの方法があり、大抵の場合は「名前ベース」の方を用いることが多いと思う。

もっとも、この「名前ベース」なバーチャルホスト設定が使えないシーンというのも存在しており、サーバエンジニアとしては両者についての理解を深めておく必要がある。

例えば下記のケースなどの場合は「IPベース」なバーチャルホスト設定を用いる(あるいは検討する)必要がある……とApacheのドキュメントに書いてある

  • 「名前ベース」のバーチャルホストに対応していないクライアント(*1)を用いている場合
  • HTTPSを使用する場合
  • 同一IPアドレスでホストされたサイトを区別できないような帯域管理手法を用いているOS/ネットワーク機器を用いる場合

現実問題として、「HTTPSを使用する場合」を除いて「名前ベース」なバーチャルホスト設定を使うケースの方が多いだろう。

HTTPSを使う場合のバーチャルホスト設定


前述の「IPベース」なバーチャルホスト設定を用いる場合の話で、「HTTPSを使用する場合」というものがあった。これについて掘り下げていこう。

「HTTPS」とは「HTTP over SSL/TLS」である。「SSL/TLS」によって暗号化された通信経路を通じて「HTTP」な通信を行う、それが「HTTPS」。より詳細な情報については「Apache の SSL/TLS 暗号化」などを参照すると良い。

「名前ベース」なバーチャルホストで必要な「Host」というHTTPヘッダは、SSL/TLSのコネクションが確立した後に送信されることになる。が、使用する証明書はSSL/TLSのコネクションを確立する最中にやりとりされる。

複数の証明書を使い分けるためにはどの証明書を使用するかを指定するための情報を送る必要があるのだが、SSL/TLSレイヤー(ハンドシェイクのタイミング)で伝達可能なのはIPアドレスとポートの情報だけである。だから、HTTPSな「名前ベース」のバーチャルホスト設定は使えないのである。

単一のサーバで複数のSSLを使う場合の手段としては、

  • 複数のIPアドレスを用いて「IPベース」なバーチャルホスト設定をおこなう
  • 単一のIPアドレスしか使えないならポートを変える
  • 使用したいSSLサーバ証明書がすべて単一ドメインのサブドメイン(*2)なら、ワイルドカードな証明書を用いる

のうちいずれかを選択する必要がある。

実用面のことを考慮すると、IPアドレスを複数取得/使用可能なよう構成して「IPベース」なバーチャルホスト設定を行う手法を選択せざるを得ない。

インタネット上からアクセス可能なサイトにおいてSSLを使用する場合は、よほど予算に余裕がある場合を除き、大抵の場合はホスティングサービスを使用するものと思われる。専用サーバサービスなど複数のIPアドレスが使用可能なプランであれば良いが、VPSなどの低価格帯サービスではそもそもIPアドレスを複数使用することが不可能である場合が多いため、「単一のサーバで複数のSSL」は断念せざるを得ないケースが多い(*3)。

仮に複数のIPアドレスを使用可能だとしても、使用するIPアドレスの数により費用がかさんだり使用可能な最大数が制限されていたりする場合もあり、状況によっては断念せざるを得ないケースもあることだろう。そうでなかったとしても、IPv4アドレスを対象とする場合は「グローバルIPv4アドレスの枯渇問題」を考慮しない訳にもいかないため、状況によっては非常に頭の痛い話となる。

なお、その他にも「X.509 V3」なSSLサーバ証明書で使用可能な「subjectAltName」という拡張項目に複数のホスト名を指定したりワイルドカード(「*」)を指定したりすればいけるらしいけど、そんなCSR作って承認してくれるSSLサーバ証明書の業者があるのか知らないし、具体的な指定方法も知らないし、それらを調べようという気もおきないので割愛する。

SNIぢゃ!SNIを使うのぢゃ!


……そう、昔はね。

いい時代になったもので、「名前ベース」なバーチャルホストでも複数のSSLサーバ証明書を運用できるようになった。キーワードは「SNI(Server Name Indication)」。


このSNIはTLSへの拡張として実装されており、SSL/TLSなハンドシェイクの際に平文でホスト名に関する情報を伝達するため、「名前ベース」なバーチャルホストでもOKになる……という寸法らしい。

一見、銀の弾丸のように見えるSNIではあるが、欠点も存在する。サーバとクライアントの両方がSNIに対応している必要がある(*4)のだ。まだ、PC方面では問題とはならないケースが大半と思われるが、スマートフォンやフィーチャーフォン、組み込み機器など比較的長期間使われ続けるデバイスがターゲットとなる場合は、慎重に検討する必要がある。

Apache/mod_sslとOpenSSLを使用した環境での具体的なコンフィグ例


というわけで、諸々の条件をクリアして無事SNIが使用できるとなれば、後はコンフィグを書くだけである。

ポイントとなるのは

  • NameVirtualHostディレクティブ
  • SSLStrictSNIVHostCheckディレクティブ

の2つのディレクティブである。

NameVirtualHostディレクティブ


HTTPな「名前ベース」なバーチャルホストを定義する場合

NameVirtualHost *:80

<VirtualHost *:80>
(snip)
</VirtualHost>

のように書くが、HTTPSの場合は

NameVirtualHost *:443

<VirtualHost *:443>
(snip)
</VirtualHost>

のように書く。

両者は排他的なものではないので、HTTPとHTTPSの双方を使う場合は

NameVirtualHost *:80
NameVirtualHost *:443

<VirtualHost *:80>
(snip)
</VirtualHost>

<VirtualHost *:443>
(snip)
</VirtualHost>

のように書けばよい。

……が、Apache2.4系の場合はこの記述自体が不要である。仮に書いたとしても単にログに警告が出るだけである。

SSLStrictSNIVHostCheckディレクティブ


これはSNIに非対応な環境からアクセスされた際の挙動を定義するディレクティブである。

デフォルトではないSNI非対応な環境からアクセスした場合、このディレクティブに「off」を指定した場合はデフォルトのSSLサーバ証明書を返すようになり、「on」を指定するとアクセスが拒否される。

具体的なコンフィグ例としては

NameVirtualHost *:443
SSLStrictSNIVHostCheck off

<VirtualHost *:443>
(snip)
</VirtualHost>

のようになる。

SNI非対応な環境を考慮する場合は「off」を定義しておく方が望ましいといえる……のであるが、デフォルトが「off」であるためいちいち指定する必要はない。

……で結局どうなの?


結局の所、Apache2.4系の場合は「NameVirtualHostディレクティブ」も「SSLStrictSNIVHostCheckディレクティブ」も定義する必要はない(書いても問題はない)。Apache2.2系の場合は「NameVirtualHostディレクティブ」のみを書いておけばよい。

具体的なコンフィグ例を必要そうな部分だけ記載する形で下記に明示しておく。

NameVirtualHost *:443
SSLStrictSNIVHostCheck off

<VirtualHost *:443>
    ServerName example.com
    DocumentRoot "/path/to/dir/document_root4example.com"
 
    <Directory  "/path/to/dir/document_root4example.com">
    (snip)
    </Directory>
 
    SSLEngine on
    SSLCertificateFile /path/to/dir/certfile4example.com.crt
    SSLCertificateKeyFile /path/to/dir/keyfile4example.com.key
(snip)
</VirtualHost>

<VirtualHost *:443>
    ServerName example.org
    DocumentRoot "/path/to/dir/document_root4example.org"
 
    <Directory  "/path/to/dir/document_root4example.org">
    (snip)
    </Directory>
 
    SSLEngine on
    SSLCertificateFile /path/to/dir/certfile4example.org.crt
    SSLCertificateKeyFile /path/to/dir/keyfile4example.org.key
(snip)
</VirtualHost>

Appendix:EV SSLを導入すべき?


その知人から聞かれた話がもうひとつあるので、それについてもボクの理解の範囲で書いておこう。なお、ボクはセキュリティの専門家ではないので、各自信頼できる専門家により提供された情報を参照して欲しい。

はじめに「SSLの役割ってなんだろう?」という点を考えてみる。

一般的には「通信を暗号化し、盗聴や改竄を防ぐ」という点が広く認識されていると思うけども、「接続先が本物である事を保障する(正当性の担保)」という役割もあって、SSLサーバ証明書を発行しているベンダーはこの両者を担保するのに様々な努力をしている。

SSLサーバ証明書には大きく分けて下記のような種類がある(*5)が、下にいけばいくほどより厳重な調査が行われたり公的書類が要求されたりする。

  • 自己署名SSL(オレオレ証明書)
  • クイック認証型SSL
  • 企業認証型SSL
  • EV SSL

乱暴な言い方をすれば、ブラウザでの表示などに違いはあるものの、基本的な機能の差はないといっても良いと思う。ということは、「ユーザへどこまでアピールしたいか」とか「提供するサービスはどの程度重要か」という点で判断するのが簡単かなと考えている。

テストサイトなど閉じた特定のクライアントからの接続を前提としている場合は、「自己署名SSL(オレオレ証明書)」が最適だろう。個人で運用しているサイトの場合も適切なケースがあるかもしれない。

個人や個人事業主などが運用しているサイトなど、比較的小規模なケースの場合は「クイック認証型SSL」が最適なケースが多いはず。法人などの場合でも、サイトの性質によっては最適なケースがあるかもしれない。

一般の企業の場合は「企業認証型SSL」を第一候補として選択するべきであろう。金融機関など様々な意味で重要なサービスを提供しているなどの場合は「EV SSL」を選択する方がよいかもしれない。

まあ、ケース毎に異なりはするのだけれど、ボクが誰かに相談された場合はこのような基準で回答するようにしている……というお話でした。

Appendix:参考文献


このエントリを書くにあたり、下記のサイトを参照した(順不同)。



*1:具体的にはHTTPの「Host」ヘッダを送ってこない……HTTP1.1はもちろんHTTP1.0の拡張仕様すら実装されていないようなアレ
*2:例えば、「www.example.com」と「sub1.example.com」の2つを同じサーバで使いたい場合など(この場合「example.com」の部分が同じだよね!)
*3:まあ、その時はそのサイト毎にVPSサービスを契約するとかするだろうけど
*4:対応状況については、Wikipediaの記事を参照するなりググるなりしてくだされ
*5:名称についてはボクの独断で勝手に付けてる部分も多々ある

2 件のコメント:

  1. さくらのVPSでバーチャルドメインを利用し、下記のように2サイトを運営しています。

    www.example.com
    smartphone.example.com

    この2つのサイトをSSLにしたい場合、
    コモンネームとして別々に2つ取得し、
    それぞれを設定すれば良いのでしょうか?

    また、SNIが上記を可能にしたということになるのでしょうか?

    返信削除
    返信
    1. > この2つのサイトをSSLにしたい場合、
      > コモンネームとして別々に2つ取得し、
      > それぞれを設定すれば良いのでしょうか?
      WebサーバがSNI対応していれば、それで大丈夫かと思います。
      事前テストをしたい場合は、Let's Encryptなどを使うと安心ですね。
      Cf.) https://letsencrypt.org/


      > また、SNIが上記を可能にしたということになるのでしょうか?
      同一サーバで複数のSSLサーバ証明書を使えるようにする……という意味では、
      SNI(いわゆるネームベース)が登場する以前でもできました。

      具体的には、

      ・IPアドレスが複数アサインされている場合(いわゆるIPアドレスベース)
       → ドメインごとにSSLサーバ証明書を取得
         (SNIの場合と似たようなイメージ)
      ・ワイルドカード対応のSSLサーバ証明書を使う場合
       → サブドメイン部分のみ可変

      といった選択肢がありました。

      ただし、IPアドレスを複数アサインするのはIPv4アドレスが枯渇している現在、
      非常に無駄ですしコストがかさみます。

      ワイルドカード対応のSSLサーバ証明書についても一般に高価ですし、
      異なるドメイン(example.comとexample.jp)の場合は使用できません。

      SNIについては、IPアドレスがひとつでよく、通常のSSLサーバ証明書が使えるという点で、
      メリットがあるものと認識しています。

      デメリットとしては、古いブラウザなどが対応していないという点でしょうか。
      運営しているサイトによっては、これが致命的になるかもしれません。
      (とはいえ、8〜9割のサイトでは問題にならないとは思いますが……)

      ……と、長々と書きましたが、さくらインターネットさんのVPSをお使いとのことなので、
      さくらのナリッジにとても良いエントリが存在するので、
      そちらを参照された方が有益かもしれません。
      (記事自体はさくらインターネットさんの共有サーバサービスを中心としていますが、
       SNIの一般的な知識として有益かと思います)

      ○SNIで1台のサーバ上に複数のSSLサイトを運用 – 前編/さくらのナリッジ
      http://knowledge.sakura.ad.jp/tech/3160/

      ○SNIで1台のサーバ上に複数のSSLサイトを運用 – 後編/さくらのナリッジ
      http://knowledge.sakura.ad.jp/tech/3167/

      削除