数年前、GitHub Securityは全体的なシークレット衛生の評価と改善に向けた取り組みを開始しました。その一環として、当時開発中だったSecret Scanning機能を試験導入しました。その結果、15,000を超えるリポジトリに20,000件以上のシークレットが散在していることが判明しました。

この数字は予想を大幅に上回るものでしたが、成功の鍵は、どのアラートが実際のリスクを表しているかを特定し、所有権を割り当て、安全にリメディエーションすることであることがすぐに明らかになりました。9か月後、オープンアラートはゼロに到達しました。

新しいSecret Scanningのお客様からよくいただく質問があります。「社内ではどのように管理しているのか?既存のシークレットを実際にどのようにクリーンアップしたのか?」

長年運営されているソフトウェア企業と同様に、GitHubのシークレット管理へのアプローチも時間の経過とともに進化してきました。GitHubは2008年に設立され、当時は今日のような集中型ボールト、自動シークレットスキャン、専用シークレット管理プラットフォームが業界全体で一般的ではありませんでした。エンジニアリングプラクティスが成熟しGitHubが成長するにつれ、従来のパターンに対するより強力なコントロール、より良いツール、体系的なリスク低減への投資を続けました。この取り組みは、セキュリティの改善、露出の低減、社内のプラクティスを業界が求める高い水準に適合させるという継続的なコミットメントを反映しています。

このブログ記事では、この取り組みで私たちが有効だった点と、皆さんが自身のシークレットをより良く保護するために適用できる戦略を紹介します。

ノイズを除去する

最初にわかったことは、アラート数はやや誤解を招くものであったということです。つまり、20,000件のアラートが20,000件の同等のリスクの問題を意味するわけではありませんでした。

データを詳しく調べたところ、わずか5つのリポジトリがこれらのアラートの約18,000件を占めており、それらのシークレットはすべて非アクティブでした。テストフィクスチャ、非アクティブ化された認証情報、テスト用に使用される偽物だが有効に見えるシークレットでした。(私たちはシークレットスキャンを構築しているため、当然テストに正当に見えるシークレットが満載のリポジトリを持っています。)

残ったのは、注意を要する2,000件以上のアラートでした。潜在的な有効な認証情報と、リスク、回転、リメディエーションに関する何千もの判断です。

シークレットはコードの中だけに存在するわけではない

シークレットのリメディエーションはソースコードだけに留まりませんでした。サポートチケット(お客様がトークンを含めることがある)、バグバウンティレポート(研究者が完全な再現を含む発見内容を公開し、トークンを使用したAPIリクエストも含む)、インシデントノート、Wikiページにシークレットが見つかりました。

カスタマーサポート、セキュリティインシデント対応、バグバウンティプログラムと連携し、共有プレイブックを開発しました。これらのワークフロー全体を通じて、リメディエーションしようとしているシークレット自体を含むイシューをオープンしたりコミットをプッシュしたりするなど、新たな問題を作らないようにする必要がありました。

段階的なアプローチ

少数のセキュリティエンジニアに1件ずつ処理させる方法では、20,000件のアラートをクローズすることはできませんでした。これを通常の運用バックログのように扱いました。新たな負債を止めてから、既存のものを反復可能で測定可能で、1人の個人的な知識に依存しないワークフローで処理していくのです。

フェーズ1: あらゆる場所で有効化し、蓄積を止める

既存のシークレットをクリーンアップする前に、新たなシークレットが積み重なるのを止めなければなりませんでした。

すべてのエンタープライズとOrganizationでシークレットスキャンとプッシュプロテクションを有効化しました。GitHub Advanced SecurityのOrganizationレベルの設定のおかげで、15,000のリポジトリを1つずつ処理する必要はありませんでした。設定を強制し、個々のリポジトリやチームがこっそりオプトアウトできないようにしました。

プッシュプロテクションはソースで新しいシークレットをブロックしました。これにより、バックログが処理速度を上回る速度で増加するのを防ぎました。

フェーズ2: 理解とトリアージ

20,000件以上のアラートをリポジトリ、シークレットの種類、経過時間で分解し、ノイズと作業を切り分けました。

調査したところ、わずか5つのリポジトリがこれらのアラートの約18,000件を占めており、それらのシークレットはすべて非アクティブでした。テストフィクスチャ、非アクティブ化された認証情報、テスト用に使用される偽物だが有効に見えるシークレットでした。(私たちはシークレットスキャンを構築しているため、当然テストに正当に見えるシークレットが満載のリポジトリを持っています。)

高ボリュームで低リスクのアラートについては、一括クローズの基準を開発しました。シークレットが専用のテストリポジトリにあり、アクティブになったことがなく、既知のテストパターンに一致する場合、確信を持って解決済みとマークできました。数日間で約18,000件のアラートをクローズしました。

難しい質問

シークレットをどのようにリメディエーションするかを戦略的に決定する必要がありました。シークレットがイシュー内にある場合、本文を編集(およびリビジョン履歴を削除する可能性がある)するか、監査証跡を保持するかをどうするか?シークレットがリポジトリにコミットされている場合、git履歴を書き換えるか?大規模にgit履歴を書き換えたことがある人なら、次に何が起こるか知っているでしょう。強制プッシュはオープンなプルリクエストを破壊し、コミットSHAを無効にし、一般的に開発者の作業を妨げます。

よくある質問は「使用しなくなったリポジトリは削除すればよいか?」でした。私たちの答えは一般的に「いいえ」でした。削除されたリポジトリはその監査証跡も失われます。そのリポジトリ内のシークレットが漏洩したり、リポジトリが侵害されたりした場合、インシデント対応時に必要となるフォレンジック記録を失うことになります。シークレットを回転させ、適切であればリポジトリをアーカイブしますが、履歴は保持します。

可能な限り、露出したシークレットを最初に回転または失効させます。より難しい質問は、残存リスクがgit履歴の書き換えを正当化するかどうか、または失効したシークレットを履歴に残しておいても安全かどうかです。これらは、各アラートで製品セキュリティチームが直面する質問や決定のタイプです。

フェーズ3: 実際に有効なものを検証する

リポジトリ内にある認証情報は何年も前に回転された可能性もありますし、今でも本番システムのロックを解除できる可能性もあります。その違いを知らなければ優先順位付けできません。

当時、シークレットスキャンにはネイティブの有効性チェック機能がなかったため、私たち自身のアプローチを構築しました。目標は限定的でした。認証情報がまだ機能するかどうかを判断し、適切な場合にはアラートをルーティングしたり適切な所有者に通知したりするのに十分なメタデータを収集することでした。

たとえば、GitHubトークンの場合、低影響のエンドポイント(GET /user)に対して1回の認証済みリクエストを行う代表的なチェックが可能です。

response="$( 
  curl -sS -w '\n%{http_code}' \ 
    -H "Authorization: Bearer $TOKEN" \ 
    -H "Accept: application/vnd.github+json" \ 
    -H "X-GitHub-Api-Version: 2022-11-28" \ 
    https://api.github.com/user 
)" 

status="${response##*$'\n'}" 
body="${response%$'\n'*}" 

case "$status" in 
  200) 
    login="$(jq -r '.login // empty' <<< "$body")" 
    echo "token appears active for GitHub user: $login" 
    ;; 
  401) 
    echo "token appears invalid or revoked" 
    ;; 
  403|429) 
    echo "unable to determine validity; rate-limited or blocked" 
    ;; 
  *) 
    echo "unable to determine validity: HTTP $status" 
    ;; 
esac 

私たちの目標は、最小限に有用な質問に答えることでした。この認証情報はまだ機能するか?誰が知る必要があるか?あいまいな応答は結論が出せないものとして扱い、リポジトリ、Organization、その他のプライベートリソースへの追加リクエストは避けました。

これは、プライバシーおよび法務チームとの緊密な連携を必要としました。「読み取り専用」の有効性チェックであっても、所有していない可能性のある認証情報に触れる場合には影響が生じる可能性があります。

この作業を手動で進めているうちに、製品チームがネイティブにソリューションを構築し、残りの作業が大幅に迅速化されました。有効性チェックは現在、GitHubのシークレットスキャンに組み込まれています。

フェーズ4: 誰が何を所有しているかを把握する

このクロスファンクショナルな作業により、所有権の問題も明らかになりました。認証情報がアクティブであることがわかっても、それを回転できるのは誰かを特定する必要がありました。

カスタマーサポート、セキュリティインシデント対応、バグバウンティプログラムと連携し、コード外で報告されたシークレットに関する共有プレイブックを開発しました。これには、作業をチームにルーティングする前にシークレット値を編集すること、認証情報がGitHubに属するものか顧客に属するものかを判断すること、影響を受ける顧客や研究者に通知して彼らが管理するトークンを回転できるようにすることなどが含まれました。これらのワークフロー全体を通じて、リメディエーションしようとしているシークレット自体を含むイシューをオープンしたりコミットをプッシュしたりするなど、新たな問題を作らないようにする必要がありました。

個人アクセストークンなどのGitHub発行の認証情報については、製品チームと連携して、アラート内にシークレットメタデータを直接表示しました。作成者、作成日、スコープなどです。これにより、トークン自体を使用して所有者を特定する必要がなくなりました。

その他の認証情報については、所有権の特定はより困難で、より深い問題が明らかになりました。すべてのリポジトリに明確な所有者がいるとは限らなかったのです。

社内のエンジニアリング標準(Engineering Fundamentalsプログラム)は、サービスに対する永続的な所有権を強制しており、サービスとリポジトリのマッピングを維持していますが、すべてのリポジトリがサービスにきれいにマッピングされるわけではありません。私たちが経験した痛みは、より広範なリポジトリ所有権イニシアチブ(GitHubのCustom Propertiesを使用)につながり、さらにすべてのシークレットが認証情報マネージャーで永続的な所有者を持つことを保証する並行した取り組みにつながりました。所有者がわからなければシークレットを回転できません。

フェーズ5: ロングテールの手動トリアージ

検証とメタデータがあっても、長い尾の部分のアラートには人間の判断が必要でした。各アラートについて。これは何へのアクセスを許可するのか?回転済みか?接続されたシステムの所有者は誰か?リメディエーションパスは何か?

却下したすべてのアラートについて、正確な処分(例: 失効済み、テストで使用、偽陽性)が記録され、リメディエーションイシューへのリンクや承認済みのセキュリティ例外などの関連コンテキストを含むコメントが残されるようにしました。

このフェーズでは、システム所有者の特定、リメディエーション状況の検証、自動シグナルのみでは不十分な残存リスクの評価について、チーム間の緊密な協力が必要でした。

フェーズ6: 体系化と説明責任の推進

パターンが出現するにつれ、作業をスケーラブルにしました。

  • アラートを社内の脆弱性管理プラットフォームにルーティングし、一元的な追跡とレポートを実現しました。
  • 認証情報の種類によってリメディエーションの手順が異なるため、シークレットの種類ごとにプレイブックを文書化し、チームがセルフサービスできるようにしました。
  • 通知を自動化し、リポジトリ所有権に基づいて適切なチームにアラートをルーティングしました。

最後の要素は説明責任でした。シークレットのリメディエーションをGitHubのEngineering Fundamentalsプログラムに結びつけ、チームが測定されるセキュリティファンダメンタルにしました。明確な期待値を設定し、チームに状況の可視性を与えました。シークレット衛生がエンジニアリングヘルスの測定方法の一部になると、組織全体で共有の責任となります。

開始から9か月後、inbox zeroを達成しました。

学んだ教訓

  1. 数字にパニックにならない。 初期のカウントは20,000件以上でしたが、90%は有効ではありませんでした。生のカウントが実際の作業範囲になることはほとんどありません。
  2. あらゆる場所で有効化し、強制する。例外なし。 部分的なロールアウトは盲点を作ります。エンタープライズレベルでシークレットスキャンとプッシュプロテクションを有効化・強制し、誰にもオプトアウトを許しませんでした。
  3. エスカレーション前に検証する。 検出されたシークレットがすべて有効とは限りません。検証により優先順位付けされたToDoリストを作成できます。
  4. メタデータは時間を節約する。 GitHub認証情報の場合、シークレットメタデータにより必要な調査作業を大幅に削減できました。サードパーティプロバイダと連携している場合は、同様のメタデータを表示するよう働きかけたり、独自のエンリッチメントレイヤを構築したりしてください。
  5. 所有権なしにリメディエーションはできない。 永続的な所有権インフラストラクチャに早期に投資してください。
  6. 検出後のワークフローを自動化する。 検出は始まりに過ぎませんが、運用上の課題はアラートのルーティング、所有者の追跡、ループのクローズでした。ワークフローレイヤに投資してください。
  7. 全員の問題にする。 セキュリティチームだけで数千件のアラートをリメディエーションすることはできません。シークレット衛生をEngineering Fundamentalsプログラムに結びつけました。リーダーシップがダッシュボードを監視すると、チームは問題を修正する時間を見つけます。
  8. 意思決定フレームワークを文書化する。 きれいなリメディエーションパスがないシークレットに遭遇するでしょう。どのように決定するかを文書化してください。回転で十分なのはいつか?履歴を書き換えるのはいつか?残存リスクを受け入れるのはいつか?

これが皆さんにとって意味すること

私たちが構築したもののほとんどを再発明する必要はありません。有効性チェック、所有権の特定、一括トリアージを含む私たちの手動ワークアラウンドの多くは、現在シークレットスキャンのネイティブ機能になっています。

今日から始める場合:

  • シークレットスキャンプッシュプロテクションをあらゆる場所で有効化・強制します。
  • バックログをリポジトリとシークレットの種類でトリアージし、ノイズであることが証明できるものは一括クローズします。
  • エスカレーション前に有効なものを検証します。
  • アラートを所有者にルーティングし、他のエンジニアリング作業と同様にリメディエーションを追跡します。

始める準備はできましたか?GitHub Advanced Securityでシークレットスキャンとプッシュプロテクションを有効化する方法を学ぶ

近日公開: 大規模なリポジトリ所有権への取り組み方、およびリポジトリとシークレットの永続的な所有権が他のすべてが依存する基盤である理由について。