2 ポイント 投稿者 GN⁺ 2024-04-10 | 1件のコメント | WhatsAppで共有
  • 2008年、GitHubのSSHログインのボトルネックを解消していた作業が、異なるユーザーが同じSSH鍵フィンガープリントを持つ異常な衝突を明らかにするきっかけになった
  • GitHubは増え続けるauthorized_keysファイルの線形検索問題を避けるため、OpenSSHにパッチを当てて鍵フィンガープリントをMySQLで参照するよう変更した
  • パッチ配布後、別のユーザーのリポジトリへSSHでアクセスできてしまう問題が発生したが、繰り返される鍵フィンガープリント衝突は単純なパッチのバグとは考えにくかった
  • 2008年5月13日にDSA-1571-1が公開され、Debian OpenSSLが約18か月にわたり予測可能な秘密鍵を生成しており、可能な鍵の数がユーザーごとに32,000個強まで減っていたことが確認された
  • 大きなセキュリティ事故はしばしば「何かおかしい」という小さな兆候から始まり、その手がかりを最後まで追える時間と能力が実際の差を生む

GitHub SSHログインのボトルネックから始まった出来事

  • 2008年3月、Engine Yardで働いていた筆者は、Rails中心のホスティング会社の顧客だったGitHubのSSHログイン性能問題を手伝うことになった
  • GitHubはgit@github.comへのSSH接続後、公開鍵認証でGitリポジトリへのアクセスを提供していた
  • 当時の鍵管理は、一般的な方法である~/.ssh/authorized_keysファイルに依存していた
    • SSHは公開鍵認証要求を受けるとauthorized_keysファイルを開き、提出された鍵に一致する項目を線形検索していた
    • 一般的なアカウントでは鍵は数個しかないため大きな問題ではないが、急成長していたGitHubでは1つの大きなファイルにすべてのSSH鍵が蓄積され、ログイン時間が目に見えて遅くなっていた

OpenSSHパッチとMySQLによる鍵参照

  • 複数の解決策を検討した末に、GitHubチームと筆者はOpenSSHをパッチし、鍵フィンガープリントを基準にMySQLデータベースから鍵を参照する方式を選んだ
  • この決定は軽々しく下せる変更ではなかった
    • OpenSSHの修正は、誤ればセキュリティ上致命的になり得た
    • 他の選択肢がさらに悪かったため、「最もましな」方法だと判断された
  • 変更作業のかなりの部分は、セキュリティを損なわないことの確認に費やされた
  • 2008年4月初旬のデプロイ後、SSHログインは高速化し、しばらくは同じ問題を心配しなくてよさそうだった

重複する鍵フィンガープリントという、あり得ないように見える症状

  • 2008年5月初旬、一部のGitHubユーザーがSSH経由で他ユーザーのリポジトリにアクセスできるという連絡がGitHubチームから届いた
  • 問題はSSH鍵認証と直接関係しており、直前にOpenSSHパッチがデプロイされていたため、筆者が書いたコードが第一の疑いの対象となった
  • デバッグの末、異なる2人のユーザーが同じ鍵フィンガープリントを持っていることが確認された
    • ユーザー同士で鍵を共有していたのでなければ、事実上起こりにくいことだった
    • 影響を受けたユーザー同士は互いを知らず、自分の鍵を公開したこともないと答えた
  • その後、別のユーザーの組み合わせでも同じ鍵フィンガープリントが見つかり、その値は前の事例とは異なっていた
    • 単なる偶然やWebアプリケーションのバグだけでは説明しにくい状況になった
  • OpenSSHパッチが原因ではないことが十分確認された後、筆者の直接的な関与は減っていった
    • 筆者はGitHubの社員ではなく、Engine Yardの他の顧客支援業務もこなす必要があった
    • GitHubチームはユーザーへの確認を続け、共通点としてDebianまたはUbuntuシステムでSSH鍵を生成していたことを把握した

Debian OpenSSL脆弱性の公開で明らかになった原因

  • 2008年5月13日にDSA-1571-1が公開され、状況は明確になった
  • DebianのOpenSSLパッケージは約18か月にわたり予測可能な秘密鍵を生成していた
  • 原因は、OpenSSLの乱数生成コード整理の過程で、Debianのメンテナーが意図せず生成可能な鍵空間を大幅に縮小してしまったことにあった
    • 特定ユーザーが生成できる鍵の候補数は「非常に膨大な数」から32,000個強まで減っていた
    • 多くのユーザーがGitHubに登録する中で、一部は推奨される慣行に従って新しい鍵を別途生成していた可能性があり、その結果として衝突が起き得た
  • この公開により、筆者のOpenSSHパッチが原因ではないという決定的な証拠が得られた

その後のDebian weak keys関連の取り組み

「何かおかしい」を追跡する時間が生んだ差

  • CVE-2008-0166となった脆弱性をLuciano Belloが正確にいつどのように発見したのか、筆者は見つけられなかった
  • 脆弱なコードを含むstable Debianリリースは公開の1年前に出ていたため、鍵衝突を見て「何かおかしい」と感じたあと、深く掘り下げる時間があったのかもしれない
  • 最近のXZバックドアも、「何かおかしい」という観察と集中的な調査が続いたことで明らかになった事例につながる
  • 重要なのは、そうした集中的な調査を実際に行える能力と時間である
    • 筆者は当時、深い調査を自分では行えなかった
    • GitHubチームも、急成長するサービスの中で機能開発と障害対応に追われていた
    • 筆者自身もEngine Yardでサポートチケットを処理していた
  • 適切なタイミングで技術、時間、エネルギーを持つ誰かが手がかりを最後まで追えるとき、大きな違いが生まれる

1件のコメント

 
GN⁺ 2024-04-10
Hacker News のコメント
  • 「Luciano Bello が、のちに CVE-2008-0166 となる脆弱性を正確にいつ、どのように発見したのかは見つけられなかった」というくだりについて、当時の IRC ログにはこう残っている
    17:23 < luciano> has really an accident. I was needing many primes numbers... 0:-)
    17:23 < Sesse> and you got the same numbers every time?
    17:25 < luciano> Sesse, not every time :P

    • このログだけを見ると、Luciano は鍵を大量生成している最中に、予想以上に衝突が多いことを不審に思ったようだ
  • 「適切な技術と時間、エネルギーを持つ人が、ちょうどよいタイミングでそこにいたのは業界にとって幸運だった」という言葉は、多くの目と「日光は最良の消毒剤」という統計を実感させる点でもある
    誰かが偶然通りかかってバグを見つけることがどれほど起こりにくく見えても、可能である以上、実際に起こる
    プロプライエタリ/クローズドなコードでは、その確率は 0 に近い

    • xz 事件はオープンソースソフトウェアの大勝利だったと思う
      誰かが異常に気づき、ソースコードとあわせて実際の状況を確認し、怪しいことがあったと分かった
      主要ディストリビューションのセキュリティ専門家に連絡して追加確認を受け、彼らもセキュリティ問題を確認したうえで即座に対応できた
      公開後は、ソフトウェアとセキュリティのさまざまな領域に専門性を持つ人々が、何がどのように行われたのか、リスクは何かを掘り下げることができた
      同じ開発者が他のソフトウェアに残した怪しいコミットも追跡・確認され、その影響分析も続いている
      各ディストリビューションは、ビルドアーカイブ周辺でどのように侵害が起きたのかという細部に敏感になり、今後似た事例を検出し防ぐ方法を探し始めた
      クローズドソースと比べると、実際の悪用が起きるまでは「ソフトウェアが少し遅い」といった報告はほとんど注目されなかった可能性が高い
      企業が最終的に突き止めたとしても、最小限の情報だけを明かす非常に慎重な説明にとどまるだろうし、それは業界全体が再発を避ける能力を大きく損なう
    • クローズドソースでもバグは常に見つかる。かなりのものはコードがなくても見つけられる
      ただし、問題を修正できなかったり、何の対応もしなかったりする可能性が高い
      ほとんどの人はバグを見つけてもどうすればよいか分からない。ずっと昔の自分もそうで、後になって、見ていたものがバグだったのだと気づいた
      ほぼ 30 年前なので細部はあいまいだが、Windows で Microsoft NetMeeting を触っていて、バッファオーバーランのエラーでクラッシュさせられた記憶がある
      当時はコンピューター初心者で、ネットワークアプリケーションのバッファオーバーフローが非常にまずいことだとは理解していなかった。業界に長くいた多くの人もそうだったようだ
      あの頃はセキュリティ問題を報告するのもはるかに難しく、場合によっては危険ですらあった
      結局必要なのは複数の要素だ。問題に遭遇すること、その問題が悪いものだと見抜けるだけコンピューティングを深く理解していること、人々が確認する場所にバグを報告できる手段、そして報告をいつ、どのように扱うかを知っているセキュリティ文化がすべて必要になる
    • プロプライエタリ/クローズドなコードでも、人々はソフトウェアのバグを常に見つけている
    • オープンソースがクローズドソースより優れていることに異論はない
      ただ、同じ文を読みながら気になったのは、Heartbleed、CVE-2008-0166、xz 事件のような深刻なセキュリティバグが、発見・公開されないままどれほど起きているのかということだ
  • この脆弱性について最近になって知った重要な事実は、この変更が性急に行われたものではなかったという点だ
    メンテナーは OpenSSL のメーリングリストに自分が見た問題を投稿し、フィードバックを求めつつ修正案を提案し、アップストリームを含むいくつかの返信も受けていた
    結果はひどい脆弱性だったが、全員が問題を見落としたとてつもなく悪い運に近いように見える

    • 当時は Debian がこのバグでかなり非難されていた印象があるが、上で述べたように協力しようとする試みはあった
      しかもアップストリームの OpenSSL コードは未定義動作を呼び出していた。そのため、コンパイラが Debian メンテナーが行ったのとまったく同じ変換をしても有効だった可能性がある
      当時はこれは学術的な話のように感じられた。まさかコンパイラがそこまで意地悪に振る舞うはずがないと思っていた
      その後は、未定義動作はそもそも避けるべきものだと、よりよく理解されるようになった
      そして 8 年後に Heartbleed が発見され、OpenSSL がどれほどまずい状態で保守されていたのかを、皆が突然思い知った
      擁護するとすれば、ほとんどボランティアに近い仕事だったということで、幸いその後の資金支援で状況は改善した
    • 悪い運というより、自動テストのカバレッジが不足していたのではないかと思う
      セキュリティ上重要な乱数生成器のコードなら、膨大な量の乱数を生成し、それらがすべて一意であることを検証するテストが本当に必要に見える
  • こういうものを読むと、人気のあるBitcoinハードウェアウォレットの一つのシード生成関数でも、こうしたことがすでに起きていたり、今後起きたりする確率はどれくらいなのか気になる
    そして、その影響がどうなるのかも気になる

    • https://www.unciphered.com/blog/randstorm-you-cant-patch-a-h...
      過去22か月にわたり、Uncipheredはブラウザベースの暗号資産ウォレット生成で広く使われているBitcoinJSと、このソフトウェアで作られた製品・プロジェクトに影響した脆弱性に取り組んできた
      長年にわたって、この脆弱性は相当数の脆弱な暗号資産ウォレットを生成させてしまった
    • そういう問題なら、かなり早く見つかりそうに思う
      SSHの脆弱性の場合、アクセスしようとしているサーバーが悪いフィンガープリントの一つを持っているかを能動的に確認する必要があるが、ウォレットの場合はネットワーク上で自動的に他人の資金へアクセスできるようになるからだ
    • https://news.ycombinator.com/item?id=6195493
    • 1億6000万ドル規模のWintermuteハックは、公開ライブラリの安全でない鍵生成が原因で起きた
      ただし、そのケースが意図的に導入された可能性は低い
    • 各鍵がシード鍵から決定論的に生成でき、シード鍵が無限ではないことを考えると、結局は時間の問題だ
      現在の技術では数百万年分の計算時間が必要だろうが、ほぼ無限に近い資金を投じて数週間以内にそれだけの計算を回せる国家アクターにとっては、不可能な範囲ではないかもしれない
      最終的には、アドレスを知っている人なら誰でもすべてのウォレットにアクセスできる時が来るかもしれない
      頭脳と資金を持つ誰かの標的になり得るなら、Bitcoinは価値を保管する場所としてそれほど安全ではない
  • 「Ezra ZygmuntowitzがGitHubにつないでくれて、GitHubチームと一緒に問題を深く掘り下げる時間を取らせてくれた」という文が面白い
    ネイティブではないせいか、GitHubチーム自体に大きな問題があるという意味にも読めて、次の文でそれを掘り下げるのかと思った
    「Lucianoが見つけていなかったら、見つかるまでどれくらいかかったのだろう」というくだりは、おそらくGitHubか大手クラウドプロバイダーくらいしか偶然ぶつからなかっただろうと思う
    ユーザー鍵を何千、何万と保存している場所は多くないからだ

    • 構文論ではこういうものを前置詞句付加問題と呼ぶ
      文を「(問題を掘り下げる)(GitHubチームと一緒に)」と読むべきなのか、「(GitHubチームに関する問題)を掘り下げる」と読むべきなのかという問題だ
      これを正しく処理するのはかなり難しいこととして知られている
    • 両方に読めるが、「problem」の後にカンマがあれば、その曖昧さはなくなっていただろう
  • 自分の理解では、OpenSSLの乱数生成器は初期化されていないスタックメモリとPIDでシードされていて、DebianはPIDだけでシードするようにしてしまった
    でもDebianのパッチがなくても、すでにかなり危険だったのでは?

    • この誤解はかなり広まっているようだ。だが実際にはそうではない
      OpenSSLのコードにはバイト列をコピーする箇所が2か所あり、そのうち一つが初期化されていないゴミ値をコピーする可能性があった。それは確かに間違いだった
      誰かがこれを直すパッチを書き、その後、LLMの助けなしに純粋に人間の無能だけで、誰かが「近くに似たコピーがもう一つあるから、これも消すべきだ」と言った
      Debianはその2つの変更を両方とも含むパッチを入れた
      結果として、OpenSSLはもはやどんなバイトもコピーしなくなった
      初期化されていないデータをコピーしないのは良いが、本物のランダムなエントロピーもプールへコピーしなくなった。なんてことだ
    • それは違う。OpenSSLは乱数生成器を**/dev/urandom**から読み取ったデータでもシードしていた
  • 「複数の可能な解決策を検討した結果、最もましな選択肢はOpenSSHにパッチを当て、鍵フィンガープリントをインデックスとするMySQLデータベースから鍵を検索させることだと結論づけた」という部分で、なぜsqliteではなくMySQLだったのだろう?
    ~/.ssh/authorized_keysへのアクセスを高速化しようとしていた状況だが、こういうケースこそMySQLが輝くように設計された状況だ
    OpenSSHにパッチを当てて~/.ssh/authorized_keys.dbを確認させるほうが、MySQLを使うようにパッチするより作業が少なかった気がする

    • 関係するマシンが多かったのだろうし、複数のデータベースを維持するより、一つのデータベースを維持するほうが普通は楽だ
      MySQLがすでに運用中だった可能性も高い。そうなら、そこにデータを保存する初期コストもなかったはずだ
      いずれにせよ、ユーザーデータベースはどこかにすでにあっただろうから
  • 弱い鍵が少数見つかったこととは別に、遅いSSHログイン時間がさまざまな理由で手がかりとして引っ張ってみる価値のあるものだという点が興味深い

  • もう一つ興味深いエピソードは、最大公約数を使って共通のpまたはq因子を持つRSA鍵を検出した事例だ: https://factorable.net/weakkeys12.extended.pdf

  • GitHubが今でもパッチ済みのopensshを動かしているのか気になる

    • 望むなら、GitHubのソースコードにかなり近いものを確認するのは難しくない
      GitHub Enterpriseのコピーを一つ買って、ファイルの難読化を解除して見て回ればいい。難読化解除は面白い挑戦で、ものすごく難しいわけでもない
      残念ながらオープンソースではないので、コードを共有したり話したりGitHubにリンクしたりはできない
      それでもGitHub Enterpriseがまだそうしたパッチを使っているなら、実運用のGitHubもそうである可能性は高い
    • 少なくとも、すべての鍵を再び~/.ssh/authorized_keysに入れる方式には戻っていないはずだ
    • GitHubはbabeldというものを使っている
      github.comの22番ポートにtelnetで接続してみると、バージョン文字列がすぐに出力される
    • 2015年にはlibsshを使っていた
      訂正すると、最初はgolangと言ったが、確認してみるとgolangを使っていたのはBitbucketだった
    • 2013年に出たOpenSSH 6.2からAuthorizedKeysCommandが追加されたので、パッチは不要だ