概要
Firefoxをバージョンアップしたところ、それまで問題なく閲覧できていたWebページで急に、「安全な接続ができませんでした」や「接続の安全性を確認できません」といったエラーが出るようになった、というお問い合わせを頂きました。
調査の結果、本質的には証明書そのものに問題があって、それまでは見過ごされていた問題が表面化したという状況であったことが分かりました。 具体的には、Firefox 37以前のバージョンでは「サイト証明書として妥当でない証明書でもサイト証明書として利用できていた」のに対し、Firefox 38以降のバージョンではそのような証明書は不正な証明書としてエラーになるようになっています。 そのため、仮に実験用の証明書であったとしても、Firefox 38以降ではサイト証明書として適切な情報を設定しておく必要があります。
この記事では、詳しい背景と問題の解決方法についての情報をご紹介します。
この問題に遭遇しうる状況
この問題は、以下のような場面で遭遇する可能性が高いです。
-
組織内専用のWebサイトで、独自のルート証明書を使用している。
-
「暗号化された通信の内容も検査できる」と謳っているネットワーク機器やファイアウォールのサービス(いわゆるSSLプロキシ)を利用している。
何故かというと、このような場面で使われる証明書はベリサインなどの認証機関に依頼して発行される物ではなく、自組織内の担当部署もしくはSSLプロキシの提供事業者自身が任意の内容で生成する物となるために、その生成過程でミスが発生しやすいからです。
「証明書の問題」となったとき、組織内専用のWebサイトでの証明書で問題が起こるというのは分かりやすいですが、SSLプロキシでも問題が起こりうるという事について理解するためは、SSLプロキシの原理を知る必要があります。
TLSおよびその前身であるSSLでは、電子署名を使った暗号化によって、クライアント(ブラウザ)と通信相手のWebサーバの間で安全な通信を行います。 例えばAmazonでの買い物でクレジットカード番号を入力する場面や、Gmailのサービス上で新しいメールを作成して送信する場面などでは、「送信されたクレジットカード番号」や「入力されたメールの内容」といった通信内容は暗号化されており、途中の通信を中継するプロキシサーバや、ビル内のネットワークを束ねる通信機器などからは情報を読み取ることができません。
これはプライバシーを守るという観点からは素晴らしい事なのですが、「社内からの通信で社外秘の情報が流出していないかどうかを監視したい」というようなニーズをシステム管理者が抱いている場合、TLSでの通信に対しては監視の目が届かないという事でもあります。
そこで登場するのがSSLプロキシです。 SSLプロキシはそれ自身がWebサイトとして振る舞うようになっており、SSLプロキシが設置されたネットワーク環境では、TLSやSSLを使った暗号化通信はすべて「ブラウザとそのSSLプロキシの間での暗号化通信」となります。 SSLプロキシはブラウザからの接続を受け付けると、ブラウザに代わって本来の接続先にリクエストを行い、SSLプロキシとWebサーバの間で暗号化通信を行います。 そして、受信した情報を今度は自分の証明書で暗号化してブラウザに返却し、ブラウザに対してあたかも本来の接続先との間で安全な暗号化通信が行われたかのように見せかけます。 この仕組みによって、SSLプロキシはブラウザと本来の接続先のWebサーバとの間に割り込んで、通信内容の監視や改変を行うという事になります。
しかし、この時SSLプロキシがブラウザに対して返す証明書は、言わば、どんなサイトの証明書としても使える「ワイルドカード証明書」でなくてはなりません。 これは大変危険な物なので、一般的な認証機関はこのような証明書を発行してくれません。 そのため、このような証明書は独自の認証局で発行しなくてはならず、利用者は自己の責任においてその独自認証局(つまりSSLプロキシの提供事業者)を信任する必要があるというわけです。
証明書の用途
前置きが長くなりましたが、ここからが本題です。
電子署名に使われる証明書には、以下のようにいくつかの種類があります。
-
TLSにおいて、通信先のWebサイトの運営者を証明するための物(サイト証明書)
-
配布されるソフトウェアについて、作成者を証明するための物(コードサイニング証明書)
-
証明書そのものの正当性を証明するための物(ルート証明書)
これらの証明書は、データ形式としてはX.509という仕様に則っており、種類の違いはKeyUsageフィールドの値の違いで表されます。
言い換えると、これらの証明書はKeyUsageフィールドなどいくつかのフィールドの値が違うだけで、データ形式の点では互換性があるという事でもあります。 そのため、証明書を解釈する側のソフトウェアの作り方次第では、証明書の本来の用途を無視できてしまいます。 実際に、Firefox 37.xおよびそれまでのバージョンでは、TLSにおいてWebサーバが送信してきた証明書が本当にサイト証明書であるかどうか(KeyUsageフィールドの値がサイト証明書として適切かどうか)を確認せずに、サイト証明書であると見なして利用していた模様です。
しかし、Firefoxの暗号化通信に関する処理にBug 1122841、Bug 1130754, Bug 1077864, Bug 975229などで段階的に変更が行われた結果、Firefox 38.0以降のバージョンでは、TLSで使用する証明書に記載されたKeyUsageフィールドの情報が仕様通りに解釈されるようになったようです。 具体的には、Firefox 38.0以降のバージョンはKeyUsageフィールドの値が以下の3つのいずれかに該当する証明書のみを有効なサイト証明書として受け入れるようになりました。
-
Digital Signature (0) / 電子署名
-
Key Encipherment (2) / 鍵暗号
-
Key Agreement (4) / 鍵交換
これに対し、例えばルート証明書専用に発行された証明書は、KeyUsageフィールドの値として以下の値のみを保持している事があります。
-
Key Cert Sign (5) / 電子証明書の検証
-
CRL Sign (6) / CRLの署名検証
そのため、ルート証明書として作成された自己署名証明書をTLS用のサイト証明書に転用した場合、Firefox 38はそれを不正なサイト証明書として認識し、冒頭に挙げたようなエラーを返すという結果になります。
対策
上記の判断はCertVerifier::VerifyCert()
というメソッドで行われていますが、実装を見ると、サイト証明書として受け入れる証明書のKeyUsageの値や判定ロジックがハードコードされており、Firefoxの設定の変更などの使用者レベルでの対処では変えられないという事が分かります。
よって、この問題を解決するには、社内Webサイトで使用している証明書やSSLプロキシで使用している証明書を、「KeyUsageに上記の3つのうちいずれかの値を持っている、サイト証明書として妥当な証明書」に入れ替える(証明書を作り直す)必要があります。 SSLプロキシの場合、提供事業者に連絡してそのような対応を取ってもらう事になります。
まとめ
Firefox 37およびそれ以前のバージョンでは問題なく通信できていたのが、Firefox 38以降で急にエラーが起こるようになった、という問題について、証明書に記載された用途の情報に起因する事例の詳細とその対策をご紹介しました。
このエントリで述べた事はFirefox 38の開発者向け情報やリリースノートには記載されておらず、実際に問題が発生している環境での詳細なログ、Firefox自体のソースコード、Firefoxのリポジトリのコミット履歴などを調査した結果ようやく明らかになりました。 また、調査にあたってはMozilla Japanにもご協力を頂く必要がありました。この場を借りてお礼申し上げます。
クリアコードではFirefoxやThunderbirdなどのMozilla製品の技術サポート事業として、これらの製品の使用において発生したこのようなトラブルに関し、ソースコードレベルでの原因究明や対策の調査を有償にて行っております。 FirefoxやThunderbirdの使用や利用でお困りの際は、お問い合わせの受け付けフォームよりご相談下さい。