Heartbleedを出発点として
原文は Salvatore Sanfilippo により に公開されました。 このブログを購読する
最近のOpenSSLのバグに対する激しい反応は理解できる。インターネット全体に突然パッチを当てなければならなくなるのは、決して楽しいことではない。さらに個人的に引っかかるのは、このバグがいかに些細なものであったかという点だ。OpenSSLの開発者を責めたいわけではないが、OpenSSLのようなソフトウェアであれば、この種の問題はもう少し巧妙なものだと普通は考えるものだ。通常はサニティチェックを*正しく*行うことに失敗するのに対し、今回のバグはmemcpy()呼び出しにおける境界チェックが完全に*欠如*していたのだから。 しかし、時々朝になって前夜に書いたコードを読み返すと、深く恥じ入ることがある。プログラマは時々失敗する。私は確実にしょっちゅう失敗する。だから必要なのは別のプロセスであって、別のOpenSSLチームではないというのが私の考えだ。 Cより安全な別の言語を提案する人もいれば、仕様が複雑すぎるから壊れているのだと主張する人もいる。どちらの主張にもおそらく一理ある。しかし、近いうちに別の仕様やシステム言語に移行する可能性は低い。だから本当の問いは、システムソフトウェアのセキュリティを今すぐ改善するために何ができるか、ということだ。 1) 金をかける。 システムのコードをより安全にすることは、投資さえあればシンプルな話だ。さまざまな企業がセキュリティ専門家を雇ってOpenSSLのコードベースのコード監査を行えば、Heartbleedのようなバグが発見される可能性は高まる。 私は、一連の自明でない条件が重なって初めて発動するような非常に複雑なバグが、真剣なコード監査の取り組みによって発見されるのを何度も見てきた。境界チェックのないmemcpy()など、セキュリティの観点からコードを分析すれば、最初に読んだ時点で目立つはずのものだ。そしてHeartbleedはそもそもどうやって発見されたかといえば、Googleで行われたセキュリティ監査を通じてだ。 おそらく、オープンソースを主にもらうだけのものと考える時代は終わったのだろう。多くの企業がGoogleなどの企業の例に倣い、OSSの開発とセキュリティのために人手を割くべきだ。 2) 静的・動的チェック。 静的コード解析は、副次的な効果として、コード監査を半自動的に行う手段でもある。 OpenSSLのようなクリティカルなシステムコードでは、静的解析をより効果的にするためにソースコードにアノテーションを付けたり、一連のルールを適用したりすることさえ、十分に許容される。 今日の静的ツールは完全な解決策ではないが、専門家であるプログラマが静的解析の出力を注意深く精査すれば、一定の価値は得られる。 もう一つの大きな助けとなるのが、Valgrindのような動的チェックだ。Cで書かれたあらゆるシステムソフトウェアは、コミットするたびにValgrindを使って自動的にテストされるべきだ。 3) ライブラリでCを抽象化する。 Cは低水準で、言語自体に安全性が組み込まれていない。しかしCの良いところは、そのむき出しの部分の上にレイヤーを築ける言語だということだ。 まともな動的文字列ライブラリは多くのバッファオーバーフローの問題を防いでくれるし、今日ではまともなプロジェクトならほぼすべてが何らかのものを使っている。しかし、それだけではない。例えば、メモリに秘密鍵のようなものが含まれるようなセキュリティ上クリティカルなコードでは、動的文字列ライブラリを拡張して、一方のバッファから他方へ暗黙のサニティチェックを行いながらだけコピーするメモリコピープリミティブを付け加えることができる。 さらに、バッファが重要なデータを含んでいる場合、論理的なパーミッションを設定して、この領域からのコピーを試みればプログラムがアボートするようにすることもできる。メモリ管理を使って重要なメモリページをさらに効果的に保護する、移植性の低い方法もあるが、移植性や予測可能性への懸念から、現実世界ではより高いCレベルでの保護の方がはるかにシンプルになりうる。 一般的に、保護なしでCを使うことを避け、その上に抽象化したライブラリを作ってプログラミングをより安全にするためには、探求できることが多くある。 4) ランダム化テスト。 単体テストではエッジケースや失敗したサニティチェックを引き起こすことはまずない。 何十年も前から知られている一つのテスト手法で、私見では十分に使われていないものがある。ファジーテストだ。 OpenSSLのバグは、Valgrindのような動的解析ツールと組み合わせて、さまざまなランダム化されたパラメータで異なる種類のOpenSSLパケットを送ることで、間違いなく発見可能だった。 私の経験では、大量のランダム化テストと、同じテストをValgrind上でプログラムを走らせながら何度も繰り返し実行する環境を組み合わせることで、そうでなければ気づかれずに済んでしまうような現実世界のバグを数多く発見できる。探求すべきモデルは多くあり、通常は完全にランダムなデータを注入するものと、有効なパケットをさまざまなランダムな方法で破損させる中間的なモデルの両方が必要になる。 この手法の典型的な例が、古いDNS圧縮の無限ループバグだ。単純な実装にランダムなパケットを少し投げ込めば、数分で見つかるだろう。 5) セキュリティ対パフォーマンスという考え方の転換。 OpenSSLが一部のシステムではmalloc/freeが遅いという理由で独自のアロケーションのキャッシュを行っているのは興味深い。これは、セキュリティ上クリティカルなコードにおいてさえ、依然としてパフォーマンスが安全性よりも過度に尊重されていることの表れだ。この具体的な事例に関して言えば、OpenSSLの開発者がmallocをラップしたとき、そのことによるセキュリティ上の影響について考えもしなかったのだろうということは認めなければならない。しかし、彼らが*一部の*システムにおけるアロケーション関数のような低水準の詳細を気にしたという事実自体が、パフォーマンスへの深い懸念の表れであり、本来はシステムの正確性や安全性についてもっと深く懸念すべきなのだ。 一般的に、今日のサーバインフラで事実上の標準となっているシステム、すなわちLinuxが、主にライセンス上の懸念から、そこらで見つかる中でも最悪のアロケータの一つを抱えてきたし、今も抱えていることは、状況を助けていない。より優れたアロケータがGPLではなくBSDライセンスだからだ。 おそらく大企業が貢献すべきもう一つの領域が、glibcのmallocに大幅な改善を提供することだろう。たとえより良い代替が利用可能であっても、glibcのmallocは多くの現実世界のシステムソフトウェアが結局は使い続けるものなのだ。 Heartbleedについての議論がより現実的なアプローチを取るようになることを願っている。なぜなら、一つだけ確かなことがあるからだ。あちこちを非難したところで、OpenSSLや他の何かの実際のセキュリティレベルが変わることはないし、将来には新たな課題が待ち受けている。例えばHTTP/2.0の実装は、セキュリティの観点から非常にデリケートな局面になりうる。 追記:実際には私は間違っていて、Glibc内部のmalloc実装はBSDライセンスなので、ライセンスの問題ではない。なぜGlibcが代わりに非常に優れており活発に開発されているアロケータであるJemallocを使っていないのかは分からない。
記事をランダムに読む
コメント
ログインしてコメントする