2012年2月5日にGoogleのセキュリティ技術者Adam Langley氏のブログ "ImperialViolet"において Revocation checking and Chrome's CRL (05 Feb 2012) が公開され、ツイッターのタイムラインや ITmediaニュースの記事「Google Chrome、SSL証明書のオンライン失効チェックを無効に」にもニュースが出て、PKI、認証、セキュリティの関係者の間ではちょっとした騒ぎになったのでご覧になった方も 多いと思います。

Langley氏のブログ記事のポイントはこんな感じ

  • SSL証明書の失効検証をOCSPやCRLをダウンロードしオンラインで検証する方法を 将来のGoogle Chromeではやめにする。
  • その代わりに各認証局の発行するCRL情報をまとめて軽量化したCRLSetという情報をGoogleからプッシュすることにより、これを失効検証に用いる。
  • このような方針をとることにした理由は、
    • オンラインで失効検証ができない際、SSL証明書を有効としてしまうブラウザが他にも存在している
    • オンライン失効検証に係る通信や処理のオーバーヘッドがかなり大きい
  • EV SSLサーバー証明書の検証については、新しい方式を使うかどうかは未定
ここで、
ITmediaニュース記事「Google Chrome、SSL証明書のオンライン失効チェックを無効に」より引用
CAが発行する証明書をめぐっては、2011年に不正なSSL証明書が発行される事件が相次いだことを受け、主要ブラウザメーカーがソフトウェアアップデートを通じて証明書を失効させる措置を取った。Googleは今後、この仕組みを活用して、アップデートを使って失効した証明書のリストを管理していく方針だとしている。
の記事などにより「将来のGoogle Chromeはオンライン失効検証は行わず、(ブラックリストによる)ソフトウェアの再起動を伴うアップデートにより失効検証する方法を採用する」と勘違いされた方もいらっしゃるようだがImperialVioletのブログを読むと「現在はFireFox、Chrome、IEなど(ブラックリストによる)ソフトウェアアップデートによる失効をきたが」と、現状を紹介しているだけで、将来サポート予定といっているのは「失効リスト(CRLSet)をプッシュする方法(Pushing a revocation list)」であると述べられている。

それでは将来的にChromeではどのように失効情報提供しようとしているのか?

Googleはこれまでの

  • (場合によっては巨大な)CRLファイルによるものでもなく
  • 通信オーバーヘッドの大きいOCSPでもなく
  • 迅速に失効情報をアップデートできないブラウザやOSのブラックリストを使う方式でもなく
もっと軽量な失効情報のプッシュ方法を考えているそうだ。

crlsetツールから将来のGoogleの失効情報プッシュを想像する

「将来のGoogleの失効情報プッシュに関して興味があれば crlset というオープンソースのツールがあるので、これを見てみろ」とブログにあるので 早速見てみた。

GoogleはCRLの代わりにCRLSetというデータにより失効情報をGoogleから配信(プッシュ)しようとしているようだ。 crlsetというコマンドでは、CRLSetと呼んでいるデータをダウンロードしたり、 ダウンロードしたファイルを表示したりということができるようだ。

最新のGoogle Goをインストールし試そうと思ったがコンパイルできない。 ここにあるコードはGo1用のコードらしくビルドしようとすると"net/html"やら"json"やらパッケージの 場所やメソッドや不整合が起きる。(ムキーーーッ!!!) できるところまで、最新のGo用に書き直したがバカバカしくなってやめてしまった。 Goはコンパイラのチェックが厳しめなので、ちょっとツールを作ったり 試してみたりするのに全く向かない。

ソースコード見てもらえばわかる通り"crlset.go"は全く大したことをしていないので、 大まかにPerlで書き直した。(Rubyにすりゃよかったと途中で後悔)

Google ChromeのCRLSetダウンロードの動き

"crlset fetch"を実行するとCRLSetをダウンロードするためのメタファイル(XML)がダウンロードされる。 "./crlset fetch"で標準出力に表示されるXMLメタファイルの 内容はこんな感じだ。

<?xml version="1.0" encoding="UTF-8"?>
<gupdate xmlns="http://www.google.com/update2/response" protocol="2.0" server="prod">
 <daystart elapsed_seconds="56280"/>
 <app appid="hfnkpimlhhgieaddgfemjhofmfblmnib" status="ok">
  <updatecheck
   codebase="http://www.gstatic.com/chrome/crlset/100/crl-set-10371439235811702542.crx.data"
   hash="" size="0" status="ok"
   version="100"/>
 </app>
</gupdate>
単にCRLSetというデータファイルの実体と、バージョン(CRLNumberみたいなものだ)を記載しているだけのメタファイルだ。

記載されたURLをダウンロードしてみると何かしらのヘッダ領域があり、ZIPファイルのデータがあることがわかる。 ZIPファイルのデータを取り出すと2つのファイルが含まれるZIPアーカイブであることがわかる。

  • manifest.json
  • crl-set
"manifest.json"の方には大した情報は無い。"CRLSet"のデータフォーマットのバージョンが記載されているだけだ。
{"name": "CRLSet", "version": "0"}

ZIPアーカイブに含まれるもうひとつの"crl-set"ファイルが今回調査すべきメインである。 Google Chromeのオープンソース部分であるChromiumの "net/base/crl_set.cc"の ソースコードにcrl-setファイルのデータ構造の定義が記載されており ざっくりとしたデータ構造の定義はこんな感じである。

・crl-setのJSON形式のメタデータのバイト長(2バイト)
・crl-setのJSON形式のメタデータの値文字列
・CAや中間CA毎にある失効情報の並び[]
 ・あるCAの失効情報(証明されてない失効リスト相当)
  ・parent_spki_sha256 - CA証明書のSubjectPublicKeyInfoのSHA256ハッシュ値バイト列
  ・失効した証明書のシリアル番号の並びの数(4byte非負整数)
  ・失効した証明書のシリアル番号情報び[]
   ・serial_length - シリアル番号のバイト数(1バイト)
   ・serial - シリアル番号のバイト列
注:"[]" は同じ要素が繰り返されることを示す
ここでJSON形式のメタデータの例を以下に示す。
{ "Version":0,
 "ContentType":"CRLSet",
 "Sequence":100,
 "DeltaFrom":0,
 "NumParents":35,
 "BlockedSPKIs":[]}
これらの情報からわかる将来のGoogle Chromeで計画しているCRLSetのpush配信の特徴を以下にまとめる。
  • 幾つかのCAまたは中間CAの発行するCRLをまとめたファイルである
  • CAの識別はCA証明書のSubjectPublicKeyInfoフィールドのSHA256ハッシュ値による
  • CRLSetはデジタル署名で保護されない
  • CRLSetはHTTP平文で配信される
  • ただの失効した証明書のシリアル番号のリストである
  • 失効時刻は記載されない
  • 失効理由などCRLエントリ拡張に相当するものは無い
  • CRLSetの世代管理は番号(Sequence値)により管理されている。(CRLのCRLNumber拡張と同等)
  • delta CRLにも対応している
この方法自体はEV SSLの推進、ガイドライン策定を行っているCA Browser Forumで提案されているものだそうだが、それが会員組織の総意を得られているのかはわからない。(PKI業界で最高に真っ当な有識者である議長のTimさんが納得するとは、ちょっと考えられないと思う。)

Googleは、CRLSetファイルをGoogleがプッシュ(=データ配信)するために認証局にCRL提供の呼び掛けを行っていくという事のようだ。

どれくらいCRLを圧縮できるのか

一つのCAが発行するCRLに対し、CRLからCRLSetに変換した場合、 ファイルサイズはどれくらい縮小されるのかを予想してみた。 鍵長やシリアル番号の桁数、拡張の有無なども影響するが、 ZIP圧縮の効果もあり概ね40%程度に圧縮されるものと思われる。

失効証明書数131,9674,596
シリアル番号バイト数1616
CRLファイルサイズ4.4MB157KB
CRLSetファイルサイズ(予想,ZIP圧縮前)2.2MB78KB
CRLSetファイルサイズ(予想)1.8MB63KB
CRLSetではZIP復元処理が2回あるのでCRLのASN.1の 処理コストと比較して5分5分といったところだろうか。

ちなみに、現在プッシュしているCRLSetはたった35のルートCA,中間CAしかサポートしていない。

ImperialVioletのブログの主張のおかしな点

「オンライン失効検証ができない場合様々な問題が起きる」として幾つか問題点を述べている。

  • 「"captive portal"(ホテルのインターネットなどでウェブブラウザで認証してから ネットが使えるようになる仕組みの事)などでは接続前はオンラインの失効検証が できない」としているが、その時点ではホテルのサービスの認証のページしか繋がる 必要が無いのであまり大きなリスクとも思えない。特定の問題なので 別に解決策もあると思う。
  • 「認証局のCRLを提供するリポジトリやOCSPレスポンダがダウンした場合、 そこが単一障害点になる」と述べており、まぁ、確かにその通りだが 認証局はそんなものダウンさせたら大問題になるし、 一般にそのための十分な冗長構成を備えているので 検証側がそれほど心配する問題ではない。
  • 「オンライン失効検証が失敗した場合に、検証が成功してしまう ブラウザがある」と述べており、過去にブラウザを幾つか調査している ようだが、検証結果にかなりの誤解と思いこみがあるんじゃないかと思う。 まず、大前提として一部のウェブブラウザではデフォルトで 失効検証が無効になっているものがあり、失効検証を有効にしてから でないとテストの意味がない。(デフォルトで失効検証ONにして ブラウザ等を提供して欲しいんですけどね。) あと、Microsoft等の製品ではオンラインで失効検証できない場合、 例えば最新のCRLを取得できない場合やOCSPレスポンスが取得できない 場合、キャッシュにあるCRLやWindows Vista以降でサポートされているLightweight OCSPのOCSPレスポンスが有効期間的に問題が無い場合これを利用して失効検証する。 また、証明書の検証結果もまた維持されるようになっており、 ブラウザの利用の途中で通信断があったとしても直近の検証結果を利用して SSLに用いる。ブラウザを再起動しないと検証結果の状態がクリアされない場合がある。 このように、設定、キャッシュ、認証結果の維持のために、 オフラインでも検証結果が「有効」となるブラウザがあるという事である。 また、オンラインかオフラインかにかかわらず失効検証できない場合は (RFC 5280的にも)証明書の検証結果は「無効」とするのが正しいし、 もしそれができないならブラウザの瑕疵だ。 いろいろ攻撃の例を述べているが、 攻撃者者がいくらオンライン失効検証ができないようにしたとしても、 失効検証ができない場合にはブラウザの証明書検証結果が「無効」になれば、 即ちフェールセーフになっていさえすれば問題がない。
  • 「以上に述べたオンライン失効検証の問題から、 「(オンライン失効検証ができない場合でも有効としてしまうブラウザがあるような)失効検証 の仕組みは、衝突するとポキッと折れてしまう車のシートベルトのようなものだ。 99%動作するかもしれないが、必要な時に動作しないのでは価値がない。」と述べている。 ブラウザがフェールセーフになっていれば99%のケースで使えれば全く問題がない。 これって、「銀行強盗、誤った本人確認や、行員の不正着服があったりして利用者の預金が 危険なので銀行の仕組みを一切やめて他の仕組みを使います。」みたいな話じゃないですか?
  • 「OCSPレスポンダへの通信の遅さや、OCSPレスポンダを使った際のIPを認証局が ログ取得できるためにユーザがどのサイトにアクセスしたのかを認証局が 知りえてしまうというプライバシーの問題がある」と述べている。 確かにOCSPレスポンダは、遅いものもあるし、検証の処理コストも高いが Lightweight OCSPを使ったり、CRLのキャッシュを使ったり オンライン失効検証のコストは小さくなるような工夫は認証局もブラウザもしている。 また、プロキシやNATもある状況で接続元IPと接続先の証明書中のホスト名が OCSPレスポンダに残っていることがそれほど大きなプライバシー問題になるだろうか?
以上の理由から、CRLやOCSPによるオンライン失効検証はやめて Google Chrome独自の失効検証方法に切り替えようとしているそうだ。 危険な話じゃないですか?

Google ChromeのCRLSetプッシュ方式の問題点

結局、CRLSetはGoogleのやる軽量なCRLアグリゲーションのようなものだ。

CRLSet失効情報が改ざんされてないと誰が証明してくれるのか?
CRLSetのGoogleからのプッシュでは、HTTPSは使わないし、デジタル署名もつかないようだ。 悪意のある中間者によりCRLSetが改ざんされたとしても、それを知る方法がない。 また、HTTPSもCRLSetを元に失効検証しているならHTTPSの証明書の有効性を判断できない。
Googleは誤ったCRLSetを発行したとしても多分罪を認めたりはしない
CRLSetのデータは各認証局から発行されているCRLを元にこれをまとめてCRLSetデータを 生成するが、CRLSet生成の過程でバグや故意の間違いがあったとしても、 有効な証明書が無効になったり、無効な証明書が有効になったりしても、 通信路の改ざん防止機能はないし、デジタル署名されているわけでもないので Googleの誤りなのか、通信路の問題か、中間者攻撃なのか判断ができない。 ニセのサイトにSSL接続して詐欺被害にあったとしても Googleは多分罪を認めたりはしないだろう。
Googleが回収したCRLを検証するかかなり怪しい
Googleは、申し入れのあった認証局からCRLをロボットにより回収し、 CRLSetを生成するようだが、個々のCRLの有効性、即ち正当な認証局から 発行されたCRLであるかを確認するかどうかはかなり怪しいと思う。 おそらく何も検証せずにCRLSetを生成するだろう。
Googleはビッグブラザーになりはしないか
GoogleがCRLを集約した(ふりをして)署名もしないCRLSetを発行することで、 Googleは証明書の検証結果を意図的にコントロールできる立場になったことに 注意しなければならない。 Googleの意向一つでCRLSetを発行でき、SSLサーバー証明書は有効にも無効にもできるのだ。
CRLSetのファイルサイズや発行周期が問題にならないか
CRLは認証局により発行周期が異なっている。毎日、3日おき、1週間おき、1ヶ月おき等 さまざまであるが、CRLSetを更新する周期はどうするのか、頻度に高いものに あわせるのか気になるところだ。また、CRLSetは複数のCRLをまとめたデータとなるので 現行の全てのルートCA、中間CAをサポートするとなれば巨大なファイルサイズになることが 予想される。(ルートCAだけでも400近く、中間CAも含めたら3倍以上になるのではと想像する。 一回のアップデートで使うかどうかもわからないほとんど無用な失効情報をダウンロードするならば 現行の必要なCRLだけをダウンロード、キャッシュして使う方が効率的ではないだろうか。
全てのCRL発行モデルに対応できるのか
CRLSetはdelta CRLには対応しているようだが、CRL/ARLモデルなど一部の 発行モデルで対応できないケースが無いだろうか。
OCSPでしか失効情報を提供していない場合どうするか
認証局によってはOCSPでしか失効情報を提供しないものもあるが、 そのような場合にはCRLSetでは対応できない。
CRLSetはCRLほどは新鮮でない
CRLSetは認証局がCRLを発行してから、ある期間を経た後に 発行されるのでCRLほどは新鮮ではない。1日以上のタイムラグは 覚悟しておく必要があるだろう。 失効情報の鮮度という意味ではOCSPもまた多くの場合、 CRLの失効情報を元にOCSPレスポンスの値が決まるケースが多いので CRLとほぼ同等かちょっと古いかだ。
結局はCRLSetを発行するGoogleの監査スキームが 最も重要なポイントなんだと思う。

おわりに

私は個人的にOCSPが嫌いでOCSPレスポンダ証明書をちゃんと検証してるよね?とか、 OCSPレスポンス検証のオーバーヘッドだってかえって大きくなるぐらいだよねとか、PKIを ややこしくしていると思っていてAdamさんのOCSPに対する文句もわからなくはないが、 かといってCRLSetプッシュもまた多くの問題を抱えていることは、このブログで 少しは知っていただけたのではないかと思う。

オンライン失効検証にこだわる必要はあまりなく、 CRLをキャッシュして使い、オフラインでも、通信障害でもCRLが取れなければ、 nextUpdate的に問題なければそれを使うというので全く問題無いように思う。 キャッシュされたCRLを使う方がCRLSetを使うよりよっぽど良い。 スマホなんかはディスク容量いっぱいあるしね、、、

参考リンク