こちらのブログは Algolia の Adam Surak(@AdamSurak) が書いた May 30 SSL incident を翻訳したものになります。
まとめと重要なポイント
- 2つの root certification authorities(ルートCA) が2020年5月30日に期限が切れた
- お客様がもし古いOpenSSLライブラリを使っていた場合、最大1.5時間、もし古い証明書ストアを使っていた場合は最大3時間影響があった
- この問題は完全に緩和されており全てのユーザーへのサービスの可用性が回復している
- この問題はOpenSSL、HTTPS、そしてPKI証明書に関連するものであるが、セキュリティインシデントではない
私たちは、2020年5月30日の日本時間の夜7時48分に、インターネットの公開鍵基盤において2つのroot certification authorities(ルートCA)が期限切れし、1つがもう一方によってクロスサインされるという珍しい事態に出くわしました。
理論上では、これは珍しいことではなく、証明書はいつでも有効期限は切れていて(一般的には1年間ですがLet’s Encryptを使っている場合は90日間)、認証局の有効期限は何年かに1度です。この2020年5月30日におきましては、認証局の有効期限が切れたことで、問題が発生し、一部のお客様がAlgoliaのAPIを利用できなくなってしまいました。
5月30日の認証局の有効期限が切れた10:48 UTCの1分後にSREチームは証明書の問題がある旨の通知を受けました。但し、Algoliaにおいては証明書は有効であり、すぐに有効期限切れが起こるようなことは想定していなかった為、予期せぬメッセージでした。Webブラウザを通して確認を行ったところ、全てが正しく動作しているように見え、APIは正しく応答を返していることが確認できました。しかし、2つ目のアラートが1つ目とは異なるサービスから通知され、それは再び証明書の有効期限が切れた、というものでした。ブラウザで行った他の検証でもサービスは有効に動いているし、正しいレスポンスをしていました。何らかの理由によってPingdomの監視サービスは証明書の有効期限が切れたと見なしていていましたが、ブラウザを通したオペレーションでは全てが正しく動作をしていました。
インフラのトラフィックに関するグラフを見ていると、APIコール数は減っていたのは目に付きましたが、ゼロになっているところはどこにもありませんでした。完全に動作しなくなっている場合よりも、動作しているところとしていないところが混在している場合の方が問題の特定と対処は難しくなる傾向があります。
どのように調査をしていくかについては、インパクトのあったサービスにMac OSのラップトップのコマンドラインからテストを行ったところから迅速に方向性を変更しました。なぜかというと、突然対象ドメインに対する単純なcurlコマンドが失敗し、証明書が有効ではないというエラーが発生していたからです。そして、更にQualys SSL Labs scannerは興味深い結果を示していました:
- Algoliaの証明書は有効
- intermediate certificate authority(中間CA)であるSectigo RSA Organization Validation Secure Server CAは有効
- パス1においてroot certificate USERTrust RSA Certification Authorityは有効で、証明書チェーンは全て有効
- パス2においてAlgoliaのルートCAであるUSERTrust RSA Certification Authorityは5月30日 10:48 UTCで有効期限が切れている
- ルートCAであるAddTrust External CA Rootは5月30日 10:48 UTCに有効期限が切れたUSERTrust RSAをクロスサイン
これは興味深い状況と言え、USERTrust RSA Certification Authorityへの有効なパスと、期限が切れてしまったパスが存在したということになります。そして、ブラウザは有効なチェーンを見つけることが出来たものの、curlコマンドはそれを見つけることが出来なかった、と。Qualys SSL Labsのテストでは、システムが動作していて、証明書のコンフィギュレーションも問題なく動作していることは判別していました。
この段階で、私たちはステータスページの情報を更新して、サポートのメールボックスの対応に専任の人間をアサインし、問題の原因と思われるものに対処をする作業をはじめました。
私たちのテスト環境では、私たちのサーバーにある期限切れの証明書を証明書チェーンから削除することが取るべきアプローチであるということを検証しました。サーバーの動作が継続し、ブラウザからのアクセスおよびcurlコマンドからのアクセスも証明書チェーンが有効でリクエストを送ること出来ることが確認できました。これによってプロダクション環境へデプロイする確証が持てました。
1台目のプロダクションサーバーに変更を適用すると、トラフィックのボリュームレベルは当初の予想通りまで回復しました。(期限切れ部分を削除した)短いバージョンの証明書が適用されるサーバーが増えていくごとに状況は改善されていき、このまま全ての対応が完了したかと思われました。しかし、そこにはまだ少数の顧客グループから証明書が有効でなくAPIに接続できないというお問い合わせがきていました。
私たちは更に新しいコンフィギュレーションでのQualys testを実施しました。
上記のように、このテストでは有効で信頼されたパスが1つと、以前にはなかった新しいパスが1つあることが分かりました。これは対応済みのAddTrust External CA rootではなくAAA Certivicate Servicesでした。どこからこの新しいパスが来たのでしょうか?USERTrust RSA Certification Authorityの証明書は3つのバージョンが存在していることが分かります:
- self-signedのルートCAは2010年に作成されている
- AddTrust External CAによってcross-signedされたバージョンは2000年に作成されている
- AAA Certificate Servicesによってcross-signedされたバージョンは2019年に作成されている
大多数のお客様がself-signedのバージョンを証明書ストアかで認識するようになっていましたが、それ以外はAddTrustは使えなくなっておりAAA Certificate Servicesのみが使えるという状況になっていました。self-signedバージョンは一定期間利用可能な証明書ストアに存在していたものであり、なぜ全てにおいてこれが認識されないのか判別できませんでした。そこでAAA Certificate Servicesの証明書をクイックに見てみたところ、2004年に作成されたものであることが分かりました。より多くの証明書ストアにより長くそこに存在しているようでした。
SREチームで2回目の私たちのpublic certificate(公開証明書)の変更のデプロイを開始し、残りのアクセスが出来ないお客様のサービスをリストアしようと試みました。その直後にお客様が再びサービスにアクセスできるようになったことを確認しました。ようやくこのインシデントに関する全てのお客様への対応が終わりました。しかし、Qualys scannerが有効だと言っている証明書であるにも関わらずなぜ一部のお客様がアクセスできなくなってしまったのでしょうか?
証明書自体は確かに有効であり、全ての証明書チェーンは正しくComodo/Sectigo certification authorityによって生成されていました(週末に証明書を失効させるのは良いプラクティスではないし、リーフ証明書のために認証機関の有効期限が切れるのも技術的に正しいとは言えませんが…)。何が想定されていなかったと言えば、それはクライアントライブラリがこの状況をどのようにハンドリングするかということ、すなわち地球上で最もHTTPSを取り扱っていると言えるOpenSSLです。調査を進めていく中で、私たちは2014年に発生した興味深いOpenSSLの不具合を発見しました。それは以下のように述べています:
Don’t use expired certificates if possible.
When looking for the issuer of a certificate, if current candidate is expired, continue looking. Only return an expired certificate if no valid certificates are found.
PR#3359 – OpenSSL source code
可能な限り期限切れの証明書は使わない。
証明書の発行元を探している間に、もし現在の候補の有効期限が切れたとしたら、継続して探し続ける。そこで、もし、有効な証明書がなかった場合のみ、期限切れの証明書を返すことがある。
この変更が実装される前は、OpenSSLはチェーン内で有効でない証明書を検出した際はその旨を宣言し、コネクションを閉じていました。そして、この変更が実装された後は、OpenSSLは期限切れの証明書をスキップして、有効な証明書を探し続けます。この小さな変更は、証明書チェーンの振る舞いに適応したものになりますが、1つの認証局が複数の認証によって署名された場合に、その中には有効なものとそうでないものがあります。全てが良いように見えますが、なぜ影響があったのでしょうか?
深堀りしていくと、この変更はOpenSSL 1.1.1 の一部に過ぎず、Open SSL 1.0.xは含まれません。これは、例えばUbuntu 16.04もしくはそれより古いバージョン、Debian 9もしくはそれより古いもの、CentOS 7もしくはそれより古いものの全てが影響を受けることを示しますが、少なくともセキュリティの観点で言えば、これらは全て一般的にサポートされているバージョンであると言えます。私たちのMac OSのラプトップではLibre SSL 2.xを使っていますが、LibreSSLも同様のことがLibreSSL 3.2.0のリリースノートに書かれていました:
* Use non-expired certificates first when building a certificate chain.
証明書チェーンを構築する際はまず期限切れでない証明書を使う
私たちは今後のMac OSで新しいLibreSSLが出てくるのを待つことになります。
しかし、なぜブラウザは証明書を正しく検証できたのでしょうか?ブラウザは独自のSSL/TLSライブラリと独自のPKI検証を備えた形で出荷されています。ChromeはBoringSSLを搭載し、FirefoxはNSSを搭載していますが、OSのSSL/TLSからは独立していて、同じ不具合もなく、更新の頻度も(OSのそれと比べて)高いのです。
そして、ここで最終的に何が起こって、なぜ一部のお客様のバックエンドの実装だけが影響を受けたのか、また、古い証明書ストアを持つシステムの中で更に古いルート証明書を必要とするものがあったのか、という全体像を掴むことが出来ました。
次のアクションとして、影響を受けた可能性のあるお客様に連絡を取って、何が起こったのかを説明しています。私たちはAlgoliaのリーフ証明書だけではなく、証明書チェーン全体で有効期限を確認できるよう、チェックツールを改善しています。そして、最後になりますが、私たちは年次のオープンソースへの寄付にOpenSSLを含めることにしました。つまり、OpenSSLのチームへ財政的な支援を行わせていただく、ということです。
FAQ
- Algoliaのサーバーに問題があったからサービスが利用出来なかったのですか?
No。サービスは全て継続して稼働をしており、古いレガシーなシステムからのみアクセスが出来なかったということになります。
- 今後、Algoliaやその他のサービスで同じことが起こらないようにするにはどうすれば良いですか?
お使いのOpenSSLのバージョンを最新の1.1.1に、そしてLibreSSLを少なくとも3.2.0にアップデートしていただく必要があります。Linuxにおいては”ca-certificates”パッケージとして配布されている証明書ストアを更新してください。
- Algoliaの証明書の有効期限が切れたのですか?
No。Algoliaの証明書の期限は切れておらず、今でも有効です。有効期限が切れたのは私たちの証明書に署名していた3つの認証局のうちの1つです。
- 他のサービス・プロバイダでも影響を受けたのですか?それともAlgoliaの固有の問題ですか?
残念ながら、他にも影響を受けたサービスがありました: Heroku, Stripe, kernel.org, Datadog, Gaudi, その他多数です。
- Algoliaのお客様への影響はどの程度だったのですか?
当社のアプリケーションクラスタの約10%において約1.5時間影響があったことを検知しています。また、最大で3時間にわたってsingle digit(1桁台)のお客様において影響がありました。但し、このインシデントの間にブラウザからくる検索クエリには影響はありませんでした。
コメント