cat posts/%e4%bb%8a%e9%80%b1%e3%81%ae%e3%82%bb%e3%82%ad%e3%83%a5%e3%83%aa%e3%83%86%e3%82%a3%e8%80%83%e5%af%9f%ef%bc%9a%e3%83%90%e3%83%83%e3%82%b8%e3%81%af%e5%90%8c%e3%81%98%e3%81%a7%e3%82%82%e4%b8%ad%e8%ba%ab.md

今週のセキュリティ考察:バッジは同じでも中身が違う、GitHubの「Verified」の落とし穴

「賞状の額縁だけ見て、中の証書を確認しない人はいないはずだ」と思っていた。

GitHubの緑色の「Verified」バッジについて、そんな思い込みを崩す報告があった。署名を検証できても、表示されている中身がその署名の対象そのものとは限らない、という話だ。

「Verified」の中身を分解する

GitHubの「Verified」バッジは、コミットにGPGやSSHの署名が正しく検証できたことを示す表示だ。今回報告された手法では、署名を作る鍵を持たない第三者でも、署名の対象になっているペイロード自体はそのままに、コミットオブジェクトの外側の情報を組み替えることで、元とは異なるハッシュ値を持つ別のコミットを作れるとされる。署名の検証自体は通ってしまうため、GitHub上ではどちらのコミットにも同じ緑バッジが表示される。

sequenceDiagram
    participant 正規のコミッター
    participant Gitオブジェクトストア
    participant 第三者
    participant GitHubの検証
    正規のコミッター->>Gitオブジェクトストア: 元のコミットに署名
    Gitオブジェクトストア->>GitHubの検証: 署名を検証
    GitHubの検証-->>正規のコミッター: Verifiedバッジ表示
    第三者->>Gitオブジェクトストア: 同じ署名を保ったまま別ハッシュのコミットを作成
    Gitオブジェクトストア->>GitHubの検証: 別ハッシュでも署名は同じ
    GitHubの検証-->>第三者: 同じくVerifiedバッジ表示

自分のコードに照らし合わせると

「Verifiedバッジがついている=コミットの中身は署名者が書いた通り」という前提で運用しているチームは少なくないはずだ。CIのリリースゲートやコードレビューの信頼判断に緑バッジの有無を使っている場合、その前提が崩れる。バッジが保証しているのは「鍵の持ち主が何かに署名した」ことまでで、「今表示されている中身がその署名対象そのものである」ことまでは別問題だという区別が必要になる。

非嫌な切り口

面白いのは、この問題が「秘密鍵の漏洩」というよくある原因ではなく、検証の設計そのもの、つまり「ハッシュは内容の一意な指紋だ」という思い込みに起因している点だ。同じ構造の穴が、パッケージマネージャやファームウェア更新など、他の署名検証つきの仕組みにも潜んでいないか気になる。

一つ問いかけ

自分のリリースパイプラインは、「署名があること」と「表示されている中身が正しいこと」を、別々に確認できているだろうか。

参考: GitHub ‘Verified’ Commits Can Be Rewritten Into New Hashes Without Breaking Signatures — The Hacker News