証明書300万件を強制失効。Let's Encrypt に一体何が起きたのか?


無料 SSL の認証局である Let's Encrypt は、有効な証明書のうち 2.6% に当たる300万件の証明書に対し、2020年3月4日に失効手続きを行うと宣言しました。しかもその事がユーザーに通知されたのは失効手続きの数時間前です。一体、Let's Encrypt に何が起きたのでしょうか? 私が調べた事を共有したいと思います。

この記事は Let's Encrypt の証明書失効に関する一連の出来事についてまとめた物です。今回の失効処理の対象となっているかどうかの確認方法等については、以下の記事をご覧下さい。

更新しました(2020/3/7)

  • 影響の度合いについての記載が正しくなかったので修正
  • 現在の Let's Encrypt の見解が正しくなかったので修正

何が起きているのか?

SSL/TLS の認証局は証明書の発行基準に基づき、適切な手続きや確認を経て電子証明書をユーザーに発行する必要があります。この手続きは認証局によっては手動で行われる事もありますし、自動で行われる場合もあります。ご存知の通り、Let's Encrypt は完全に自動的な方法で証明書を発行します。

この自動的な発行を行うシステムは Boulder というサーバーソフトウェアによって行われるのですが、Boulder にバグが見つかってしまい、Let's Encrypt の発行基準とは異なる動作をしている事が分かってしまいました。そのため Boulder を修正するとともに、すでに発行されてしまった証明書を失効させなくてはならなくなりました。失効させる証明書の数は約 300万件との事であり、これは現在有効な証明書の約 2.6% に当たるとの事です。

この問題は日本で利用しているユーザーにも直接的に影響を及ぼし、大手プロバイダでは多数の一部の顧客サイトに障害が発生しました。以下はさくらインターネットのお知らせです。

(2020/03/07 18:50 追記: 引用したさくらインターネットさんのお知らせでは影響は限定的だったとの事です。お詫び申し上げます)

どのような問題だったのか?

Boulder は証明書発行のリクエストを処理する CA サーバーソフトウェアです。Let's Encrypt を利用するために .well-known というディレクトリを用意しているサーバーは多いと思いますが、ここを読みに来ているのが Boulder です。MPL2 ライセンス で供給されているオープンソースソフトウェアであり、github で公開されています。

CA サーバーは DNS の CAA レコードを参照する必要がありますが、Boulder の CAA レコードの参照機能にバグがあり、許可されていないクライアントに対しても証明書を発行してしまった可能性がある事が分かってしまいました。

CAA レコードとは?

DNS の CAA レコードは SSL/TLS 証明書の発行を許可する認証局を、ドメイン所有者が宣言する仕組みです。A レコードや MX レコードに比べればマイナーなレコードですし、これを設定しなかったからと言って SSL/TLS 証明書が利用できないという事は現在はありません。ですので利用していないサイトの方が多いのが現状でしょう。しかし 2017年に CA / Browser Forum によって CAA レコードの確認が必須となり、多くの(と言うよりたぶんすべての)認証局はこの規定に従うようになりました。CAA レコードの詳細は以下のリンク先等を見て下さい。

CA / Browser Forum とは?

CA / Browser Forum は、SSL/TLS 証明書の運用方法について、ベストプラクティスを推進する協会です。CA / Browser Forum が策定した基準に従い Microsoft や Google 等のウェブブラウザベンダーは、認証局に対してルート証明書の搭載要件を提示します。搭載要件が満たされない認証局は、ウェブブラウザにルート証明書を搭載してもらえなくなってしまうのです。ウェブブラウザにルート証明書が搭載されない認証局の証明書は、自己署名証明書と同程度の価値しかなくなります。このような証明書を利用しているサイトにアクセスしても警告画面が表示されたり、アドレスバーが赤くなったりする事はご存知の通りです。

従って Let's Encrypt のような認証局にとっては、CA / Browser Forum の規定に従っているかどうかは大変重要な課題です。

失効手続きとは?

Let's Encrypt は Boulder のバグにより正しく CAA レコードを参照せずに発行してしまった証明書を失効させる事にしました。証明書が失効されるとブラウザが警告状態になってしまいます。以下は Google Chrome で失効された証明書のサイトを表示している様子です。

これがどのようなプロセスによって実現されるかについて説明します。

ユーザーの操作によって SSL/TLS で保護されたサイトへのアクセスが要求されると、ウェブブラウザはまずは証明書を取得し、それが妥当な証明書かどうかを確認します。有効期限が切れていたり、自己署名証明書だったりした場合には警告を表示するのですが、さらに各認証局が用意している OCSP サーバーと通信し、証明書が失効されていないかどうかを確認します。OCSP サーバーは失効した証明書のリストを管理するサーバーであり、クライアントからの要求に応じて証明書が失効されていないかどうかを返します。この仕組みにより、認証局は発行済の証明書をほぼリアルタイムに失効させる事が可能です。

OCSP サーバー と通信する

実際に OCSP サーバーと通信してみましょう。対象はどこでもいいですが、https://qiita.com/ で利用している証明書を確認してみます。クライアントには openssl のコマンドラインツールを利用します。

まずは qiita.com の証明書を取得します。

# 証明書を取得
openssl s_client -connect qiita.com:443 </dev/null 2>/dev/null > qiita.crt

このコマンドでは証明書の他に余分な物も混じってしまいますが、シグニチャを利用して切り出されますので以後のコマンドでは問題ありません。

次に取得した証明書からシリアル番号を取得します。

# 証明書のシリアル番号を取得
openssl x509 -serial -noout < qiita.crt

2020/3/6 現在において、以下の出力が得られました。

serial=0833104DAF19FA76AC743A6022DDCB71

次にこの証明書の失効情報を管理している OCSP サーバーの情報を取得します。

# 証明書の OCSP サーバーを取得
openssl x509 -ocsp_uri -noout < qiita.crt

以下の出力が得られました。

http://ocsp.sca1b.amazontrust.com

これで準備は整ったので、実際に OCSP サーバーに接続し、qiita.com の証明書が失効されていないかどうかを確認します。以下のコマンドです。

# OCSPサーバーのホスト名を設定
ocsp_host=ocsp.sca1b.amazontrust.com

# シリアル番号を設定
serial=0833104DAF19FA76AC743A6022DDCB71

# 証明書のファイル名を設定
cert_file=qiita.crt

# OCSP サーバーと通信し、ステータス行で絞り込む
openssl ocsp -noverify -no_nonce -issuer $cert_file -serial "0x$serial" \
  -url http://$ocsp_host -text -header 'HOST' "$ocsp_host" |
  grep 'Cert Status:'

(2020/3/11 追記: 上記スクリプトの不正確な記述を修正しました。詳細はコメントを参照して下さい)

以下の出力が得られました。

    Cert Status: good

ステータスが good なので qiita.com の証明書に問題が無い事を確認できました。失効されている証明書の場合、ステータスは revoked になります。

今行ったテストと同等の事はウェブブラウザの内部でも行われており、失効を確認した場合には警告画面を表示してユーザーに注意を促します。認証局側は自分が管理している OCSP サーバーのデータをいつでも更新できますので、失効情報を即座に反映させる事ができます(ただし、ブラウザが失効情報を一定期間キャッシュする事はあります)。

このような仕組みはフィッシングサイトのような物を早急に潰すために有効とされています。

なぜこれほど急いで処理する必要があったのか?

ここまでで、以下の事について説明しました。

  • Boulder にバグがあり、それが原因で Let's Encrypt は不適切な証明書を発行してしまった
  • そのために約 300万件の証明書を失効した

大体の事情についてはご理解頂けたのではないかと思うのですが、なぜこれほど急に失効する必要があったのかという事について説明します。Boulder のバグの詳細が分かったのは 2/29 であり、Let's Encrypt は即座に証明書の発行を停止しました。その後、Boulder を修正して証明書の発行業務を再開しましたが、3/3 には約 300万件の証明書を 3/4 に失効する事を発表します。この辺の時系列をまとめます。

日時(UTC) 日時(JST) 内容
2020/02/29 UTC 03:08 2020/02/29 JST 12:08 バグの確認
2020/02/29 UTC 03:10 2020/02/29 JST 12:10 証明書発行の停止
2020/02/29 UTC 05:22 2020/02/29 JST 14:22 バグ修正完了・証明書発行の再開
2020/02/29 UTC 14:58 2020/02/29 JST 23:58 これまでの経緯についてフォーラムで説明
2020/03/03 UTC 16:01 2020/03/04 JST 01:01 (証明書失効の件について私の所にもメールが届き、初めて事態を知った。嘘だと思いつつ情報収集し、慌てて作業開始)
2020/03/03 UTC 17:38 2020/03/04 JST 02:38 3/4 に該当する証明書を失効する事が、フォーラムでも告知される
2020/03/03 UTC 23:22 2020/03/04 JST 08:22 これまで 3/4 とはどこの地域の 3/4 で、3/4 の何時からなのかの告知が不明瞭だった。スタッフが 3/4 とは UTC における 3/4 であり、3/4 の 00:00 UTC から処理を開始する事をフォーラムで告知した
2020/03/04 UTC 00:00 2020/03/04 JST 09:00 失効が開始される。日本でも多数の証明書が実際に失効された

以上のような流れであり、これ以上ないぐらい慌ただしい話しです。これほど急な話しなのに当初はタイムゾーンや開始時刻についての明言が無くさらに混乱を招く事になったようです。

当然批判も大きかったのですが、Let's Encrypt はどうしても 3/4 に開始し 3/5 の 03:08 UTC までには処理を完了したかったのです。この事についてのユーザーからの質問に対し、フォーラムのコミュニティリーダーは It’s not Let’s Encrypt choice, it’s an obligation (これは Let's Encrypt の選択ではなく義務である)と説明しています。

この義務というのは Let's Encrypt の発行基準の事を示していると思われます。先に説明した通り、各ブラウザベンダーにルート証明書を搭載してもらうために、Let's Encrypt は CA / Browser Forum の策定基準に従わなくてはなりません。その策定基準は Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates (公的に信頼された証明書の発行と管理のベースライン要件) という書類に書かれており、以下の URL から閲覧可能です。

2020/3/5 現在において全 62 ページに及ぶ書類なのですが、この中の 30ページから 31ページに掛けて以下のような記述があります。

The CA SHOULD revoke a certificate within 24 hours and MUST revoke a Certificate within 5 days if one or more of the following occurs:

筆者訳: 認証局は次の1つ以上が発生した場合に 24時間以内に証明書を取り消すべきであり (SHOULD)、5日以内に証明書を取り消さなくてはならない (MUST)

この文に続き 11 項目程が続くのですが、その中の 7 番目の項目に以下の記述があります。

The CA is made aware that the Certificate was not issued in accordance with these Requirements or the CA's Certificate Policy or Certification Practice Statement;

筆者訳: 証明書がこれらの要件、または認証局自身の証明書ポリシー、または CPS に従って発行されなかったことを、認証局が認識した時

このように明確に期日が指定された義務であり、この義務を全うしないと Let's Encrypt はベースライン要件を満たせなかったという事になり、最悪各ウェブブラウザにルート証明書を搭載してもらえなくなってしまう可能性があります。今回、2/29の 03:08 UTC に問題を確認した事になるので、3/5 の 03:08 UTC が期限という事になります。

以上の事情から、早急な対応が必要だったと考えられます。

そして今(2020/3/6)

(2020/03/07 18:15追記: この項目が旧版では不正確&不十分でした。お詫び申し上げます)
(2020/03/09 15:30追記: 訳の修正と、原文へのリンクを追加しました)

慌ただしい 3/4 から 2日ほど経ちました。2020/03/05 01:20 UTC の時点(失効手続き開始の約25時間後で、ベースライン要件で定められた期日の約2時間前)で Let's Encrypt 側の進捗は以下のように発表されています。

  • 170万件を超える証明書は発表後48時間以内に交換された
  • 100万を超える証明書は交換されない可能性が高いが、期限までに失効しない事が利益であると判断した
  • 2020/03/05 03:00 UTC のコンプライアンス期間前に以下の証明書を失効した
    • 新しい証明書に置き換えられた 1,706,505 件
    • CAA レコードで明確に Let's Encrypt からの発行を禁止している 445件
  • 失効する事でユーザーが必要以上に混乱する事は無いと自信を持てたら、今後もより多くの証明書を取り消す予定である
  • 上記の原文

ひとまずは現在まだ失効されていない証明書については見合わせる事になったようです。しかし、今現在問題が無くても以前に取得した失効対象の証明書は今後も失効される可能性があると考えなくてはならないと思います。失効対象の証明書をまだ使っている場合は、なるべく早めに対応する事をお勧めします。

最後に

3月4日は実に慌ただしい思いをしたのですが、その過程でアップした記事は筆者の予想をはるかに超えて多くの人に読んで頂き、沢山のいいねやストックを頂きました。改めて読み直してみると色々不十分な所もあり、リライトしましたので見て頂けると幸いです。

また、沢山の方に見て頂いた事が、事態をより詳しく調査してみるモチベーションに繋がりました。読んで下さった皆さん、誠にありがとうございました。