GitHub がシークレットスキャンを活用して受信トレイゼロを達成した方法
本文の状態
日本語全文を表示中
詳細モードで約16分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
GitHub Blog
GitHub Security は、開発中のシークレットスキャン機能をパイロット試験し、15,000 以上のリポジトリから 2 万を超える機密情報を発見。リスクの特定と責任者の割り当てにより、セキュリティ対策の効率化を実現した。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
数年前、GitHub Security は全体的なシークレットの衛生状態を評価し改善するためのイニシアチブを開始しました。その取り組みの一環として、当時開発中だった Secret Scanning 機能をパイロット導入しました。その際、15,000 以上のリポジトリにまたがる 2 万 2 千件を超えるシークレットが見つかりました。
この数は私たちが想定していたよりもはるかに多いものでしたが、すぐに成功の鍵は、実際のリスクとなるアラートを特定し、責任者を割り当て、安全に修正することにかかっていることが明確になりました。9 ヶ月後には、未解決のアラートはゼロとなりました。
新しい Secret Scanning の顧客からはよく、「社内ではどのように管理しているのですか?既存のシークレットを実際にどう cleanup したのですか?」と尋ねられます。
多くの長寿ソフトウェア企業と同様に、GitHub のシークレット管理へのアプローチも時間とともに進化してきました。GitHub は 2008 年に設立されましたが、当時は業界全体で一般的だった集中型 vault(金庫)、自動化されたシークレットスキャン、および専用のシークレット管理プラットフォームはまだ存在しませんでした。エンジニアリングプラクティスが成熟し GitHub が成長するにつれて、レガシーなパターンに対するより強力な制御、優れたツール、体系的なリスク低減への投資を継続してきました。この取り組みは、セキュリティの向上、暴露の削減、そして業界全体で期待される高い基準に内部実践が合致していることを保証するという、私たちの継続的なコミットメントを反映しています。
本ブログ記事では、この取り組みの中で成功した方法と、ご自身のシークレットをよりよく保護するために適用できる戦略について共有します。
ノイズの排除
最初に発見したのは、アラート数が少し誤解を招く可能性があるということでした。つまり、20,000 件のアラートがあったからといって、すべてが同等のリスクを持つ問題であるわけではありません。
データを詳しく掘り下げてみると、そのアラートの約 18,000 件がたった 5 つのリポジトリに集中していることがわかりました。そして、それらのシークレット(秘密情報)はすべて非アクティブなものでした:テスト用の固定データ、無効化された認証情報、テスト用に作成された偽物だが本物らしく見えるシークレットなどです。(私たちはシークレットスキャンニングの仕組みを構築しているため、当然ながらテスト環境には本物らしいシークレットで満たされたリポジトリが存在します。)
残りの 2,000 件ほどのアラートが対応を要するものでした。これらは潜在的に現在も有効な認証情報であり、リスク評価、回転(ローテーション)、および修復に関する数千の判断が必要となるケースです。
シークレットはコードの中だけにあるわけではない
シークレットの修復対象はソースコードだけではありませんでした。サポートチケット(顧客が偶発的にトークンを含めることがある)、バグ報奨金プログラムへの報告書(研究者が発見した内容を完全な再現手順とともに開示し、使用されたトークンを含む API 要求などを含む)、インシデントノート、そしてウィキページにもシークレットが見つかりました。
私たちは顧客サポートチーム、セキュリティインシデント対応チーム、およびバグ報奨金プログラムと連携して、共通のプレイブックを開発しました。これらのすべてのワークフローにおいて、修復しようとしているシークレット自体を含むイシューを開設したりコミットを行ったりするなど、新たな問題を引き起こさないように確保する必要がありました。
段階的なアプローチ
20,000 のアラートを数人のセキュリティエンジニアに一つずつ処理させることで解決しようとはしませんでした。これは他の運用上のバックログと同様に扱い、新規の負債(デット)を発生させないことと、すでに存在するものを反復可能で測定可能、かつ特定の個人の組織内知識に依存しないワークフローで解消することを目指しました。
フェーズ 1:全社的に有効化し、蓄積を止める
既存のシークレット(機密情報)をクリーンアップする前に、まず新たなものが積み上がらないようにする必要がありました。
GitHub Advanced Security の組織レベルの設定を活用して、すべての企業および組織でシークレットスキャンとプッシュ保護を有効化しました。これにより、15,000 件のリポジトリすべてを手作業で一つずつ設定していくような手間のかかる作業は不要となりました。個々のリポジトリやチームがこっそりとオプトアウトできないよう、この設定を強制しました。
プッシュ保護によって、新たなシークレットの生成源でブロックされました。これにより、バックログが解消する速度よりも速く増加することを防ぎました。
フェーズ 2:理解とトリアージ
ノイズ(不要な情報)と実作業を区別するために、20,000 を超えるアラートをリポジトリ、シークレットの種類、および経過年数ごとに分類しました。
詳細を調査した結果、約 18,000 のアラートがたった 5 つのリポジトリに集中していることがわかりました。また、それらすべてのシークレットは非アクティブ状態でした:テスト用の固定データ(フィクスチャ)、無効化された認証情報、およびテスト用に使用されている偽物だが有効に見えるシークレットです。(私たちはシークレットスキャン機能自体を開発しているため、当然ながらテスト用リポジトリには本物そっくりのシークレットが大量に含まれています。)
For high-volume, low-risk alerts, we developed criteria for bulk closure. If a secret was in a dedicated test repository, had never been active, and matched a known test pattern, we could confidently mark it resolved. In a matter of days, we closed out roughly 18,000 alerts.
The hard questions
We had to make strategic decisions about how to remediate secrets. When a secret lives in an issue, do you edit the body (and potentially remove revision history), or preserve the audit trail? When a secret is committed to a repository, do you rewrite git history? Anyone who's tried rewriting git history at scale knows what happens next: force-pushes break open pull requests, invalidate commit SHAs, and generally interrupt developers.
A common question was: "Can we just delete the repository if it's no longer in use?" Our answer was generally no. A deleted repository takes its audit trail with it. If a secret in that repository was ever leaked or the repository was ever compromised, you lose the forensic record you'd need during incident response. Rotate the secret, archive the repository if appropriate, but keep the history.
Whenever possible, we rotate or revoke the exposed secret first. The harder question is whether the residual risk warrants rewriting git history, or whether a revoked secret in history can safely be left in place. These are the types of questions and decisions present with each alert that product security teams wrestle with.
フェーズ3:実際に稼働しているものを検証する
リポジトリに置かれた認証情報が何年も前にローテーション済みである可能性もあれば、まだ本番システムへのアクセスを許可し続けている可能性もあります。その違いがわからないまま優先順位をつけることはできません。
当時、シークレットスキャンにはネイティブな有効性チェック機能がありませんでしたので、独自の手法を開発しました。目的は限定的で、認証情報が現在も有効かどうかを判断し、必要に応じてアラートをルーティングしたり適切な所有者に通知したりするために十分なメタデータを収集することです。
例えば、GitHub トークンの場合、代表的なチェックとして、低影響のエンドポイントである GET /user に対して単一の認証済みリクエストを実行します:
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
翻訳全文
覚えておいてください、私たちの目標は、最も有用な最小限の質問に答えることでした:この認証情報はまだ有効か、そして誰がそれについて知る必要があるのか?曖昧な回答は結論が出ないものとして扱い、リポジトリや組織、その他のプライベートリソースに対する追加の問い合わせを避けるようにしました。
これには、プライバシーおよび法務チームとの緊密な連携が必要でした。自分が所有していないかもしれない認証情報に触れる場合、「読み取り専用」の有効性確認であっても影響を及ぼす可能性があります。
手作業でこのプロセスを進める一方で、製品チームはネイティブにソリューションを開発し、残りの作業を大幅に短縮しました。現在、有効性のチェック機能は GitHub のシークレットスキャンニング(secret scanning)に組み込まれています。
フェーズ 4:誰が何を所有しているか特定する
この横断的な取り組みにより、所有権に関する問題も浮き彫りになりました。認証情報が有効であることを知った後でも、それを更新できるのが誰かを特定する必要がありました。
コード外で報告されたシークレットに対する共有プレイブックを開発するため、カスタマーサポート、セキュリティインシデント対応チーム、バグバウンティプログラムと連携しました。これには、作業を各チームにルーティングする前にシークレットの値を隠すこと、認証情報が GitHub 側のものか顧客のものかを判断すること、影響を受ける顧客や研究者を通知して彼らが管理するトークンを更新できるようにすることが含まれます。これらのすべてのワークフローにおいて、修正しようとしているシークレット自体を含むイシューを開いたりコミットを行ったりするなど、新たな問題を引き起こしていないことを確保する必要がありました。
GitHub が発行したパーソナルアクセストークンなどの認証情報については、製品チームと連携して、アラート内に作成者、作成日時、およびスコープといったシークレットのメタデータを直接表示できるようにしました。これにより、トークン自体を使用せずに所有主を特定する必要がなくなりました。
それ以外のケースでは所有権の特定が難しく、これはより深い問題、すなわちすべてのリポジトリに明確な所有者がいるわけではないという課題を浮き彫りにしました。
当社の内部エンジニアリング基準(Engineering Fundamentals プログラム)はサービスの永続的な所有権を義務付けており、サービスとリポジトリのマッピングも維持していますが、すべてのリポジトリがきれいにサービスに対応しているわけではありません。この経験から生じた課題に対し、GitHub のカスタムプロパティを活用したより広範なリポジトリ所有権の取り組みを開始すると同時に、認証マネージャー内のすべてのシークレットに永続的な所有者を確保する並行プロジェクトも推進しました。所有者が見つからない限り、シークレットをローテーションすることはできません。
フェーズ 5:長期化するアラートに対する手動トリアージ
検証とメタデータの活用にもかかわらず、多くのアラートには人間の判断が必要でした。各ケースについて、この権限が何へのアクセスを付与するのか、ローテーション済みか、接続システムの所有者は誰か、そして是正措置の経路はどうなるのかを確認しました。
却下したすべてのアラートについては、取り消し、テスト環境での使用、偽陽性など、正確な処理結果(ディスポジション)を記録するとともに、是正措置に関する課題へのリンクや承認されたセキュリティ例外など、関連する文脈を含むコメントも併記しました。
このフェーズでは、システム所有者の特定、修復状況の確認、自動化されたシグナルだけでは不十分な場合の残存リスクの評価を行うために、チーム間の緊密な連携が求められました。
第 6 フェーズ:仕組み化と責任の明確化
パターンが見えてくるにつれ、作業をスケーラブルなものにしました。
アラートを内部の脆弱性管理プラットフォーム(vulnerability management platform)へルーティングし、中央集権的な追跡と報告を実現しました。
異なる種類の認証情報には異なる修復手順が必要です。シークレットの種類ごとにプレイブックを文書化し、チームがセルフサービスで対応できるようにしました。
通知の自動化を行い、リポジトリの所有権に基づいてアラートを適切なチームへルーティングしました。
最後のピースは責任の明確化です。シークレットの修復を GitHub のエンジニアリング基礎プログラム(Engineering Fundamentals program)に紐付け、チームが評価されるセキュリティの基本事項と位置づけました。明確な期待値を設定し、各チームに状況の可視性を提供しました。シークレットの衛生状態がエンジニアリングの健全性の評価基準の一部となることで、組織全体での共有責任となります。
開始から 9 ヶ月後、受信トレイゼロを達成しました。
教訓
数値を見てパニックにならないこと。初期のアラート数は 20,000 件以上でしたが、その 90% は有効ではありませんでした。生数のカウントは、実際の作業範囲のほとんどにおいて真実を表すものではありません。
例外を許さず、あらゆる場所で有効化し強制すること。部分的な展開は盲点を生みます。私たちは、誰にもオプトアウトを許可せず、エンタープライズレベルでシークレットスキャンとプッシュ保護(push protection)の有効化と強制を行いました。
エスカレーションする前に検証してください。検出されたすべての秘密情報が実際に使用されているわけではありません。検証を行うことで、優先順位付けされたToDoリストを作成できます。
メタデータが数時間を節約します。GitHub の認証情報については、秘密情報のメタデータによって必要な調査作業を大幅に削減できます。サードパーティのプロバイダーと連携する場合は、同様のメタデータの公開を促すか、独自のエンリッチメント層を構築してください。
所有権(オーナーシップ)がなければ修復はできません。耐久性のある所有権インフラストラクチャには早期に投資してください。
検出後のワークフローを自動化してください。検出はスタート地点に過ぎず、実際の運用上の課題はアラートのルーティング、所有者の追跡、そしてループの完了です。ワークフロー層への投資を行ってください。
これは全員の問題にしましょう。セキュリティチームが数千件のアラートを単独で修復することはできません。秘密情報の衛生管理をエンジニアリング基本方針プログラムと結びつけました。リーダーシップがダッシュボードを確認するようになると、チームは問題を修正するための時間を確保できるようになります。
意思決定の枠組みを文書化してください。健全な修復パスを持たない秘密情報に直面することがあります。どのように判断するかを文書化してください:回転(ローテーション)で十分なのはいつか?履歴を書き換えるのはいつか?残存リスクを受け入れるのはいつか?
あなたにとっての意味
私たちが構築したものの多くをゼロから作り直す必要はありません。有効性チェック、所有権の特定、バッチトリアージなど、多くの手動ワークアラウンドは現在、秘密情報スキャンのネイティブ機能として実装されています。
今日から始める場合:
すべての場所で秘密情報スキャンとプッシュ保護を有効化し、強制してください。
リポジトリおよび秘密情報の種類ごとにバックログをトリアージし、ノイズであると証明できるものは一括でクローズしてください。
エスカレーションする前に、実際に稼働しているものを検証してください。
アラートを所有者にルーティングし、他のエンジニアリング作業と同様に修正の進捗を追跡します。
開始準備はできましたか?GitHub Advanced Security を使用してシークレットスキャンとプッシュ保護を有効にする方法を学びましょう。
近日公開:大規模なリポジトリ所有権への取り組みと、永続的なリポジトリおよびシークレットの所有権が他のすべての基盤となる理由について。
本記事「How GitHub used secret scanning to reach inbox zero」は、The GitHub Blog で最初に掲載されました。
原文を表示
Several years ago, GitHub Security launched an initiative to assess and improve our overall secrets hygiene. As part of that effort, we piloted the Secret Scanning capability that was under development at the time. That’s when we found more than 20,000 secrets spread across our 15,000+ repositories.
The number was significantly higher than we anticipated, but it quickly became clear that success would depend on identifying which alerts represented real risk, assigning ownership, and remediating them safely. Nine months later, we reached zero open alerts.
New secret scanning customers often ask us: “How do you manage this internally? How did you actually clean up your existing secrets?”
Like many long-running software companies, GitHub’s approach to secrets management evolved over time. GitHub was founded in 2008, before today’s centralized vaults, automated secret scanning, and dedicated secrets-management platforms were common across the industry. As engineering practices matured and GitHub grew, we continued investing in stronger controls, better tooling, and systematic risk reduction for legacy patterns. This work reflects our ongoing commitment to improving security, reducing exposure, and ensuring our internal practices meet the same high standards we expect across the industry.
This blog post shares what worked for us during this effort, and highlights strategies you can apply to better protect your own secrets.
Cutting out the noise
The first thing we discovered was that the alert count was a bit misleading—i.e., 20,000 alerts did not mean 20,000 equally risky problems.
When we dug into the data, we discovered that just five repositories accounted for roughly 18,000 of those alerts, and every one of those secrets was inactive: test fixtures, deactivated credentials, and fake-but-valid-looking secrets used for testing. (We build secret scanning, so naturally we have repositories full of legitimate-looking secrets in tests.)
That left over 2,000 alerts that needed attention: potential live credentials and thousands of decisions about risk, rotation, and remediation.
Secrets don’t just live in code
Secret remediation touched more than source code. We found secrets in support tickets (customers occasionally include tokens), bug bounty reports (researchers disclose what they found with complete reproductions, including API requests with tokens used), incident notes, and wiki pages.
We partnered with customer support, security incident response, and our bug bounty program to develop shared playbooks. Across all these workflows, we had to ensure we weren’t creating new problems, like opening issues or pushing commits containing the very secrets we were trying to remediate.
Our phased approach
We were not going to close 20,000 alerts by asking a few security engineers to grind through them one by one. We treated it like any other operational backlog: stop new debt, then work down what already exists with a workflow that’s repeatable, measurable, and not dependent on one person’s institutional knowledge.
Phase 1: Enable everywhere, stop the accumulation
Before cleaning up existing secrets, we had to stop new ones from piling up.
We enabled secret scanning and push protection across all of our enterprises and organizations. Thanks to GitHub Advanced Security’s organization-level settings, this wasn’t a repository-by-repository slog across 15,000 repositories. We enforced the setting so individual repositories and teams could not quietly opt out.
Push protection blocked new secrets at the source. That kept the backlog from growing faster than we could burn it down.
Phase 2: Understand and triage
We broke down the 20,000+ alerts by repository, secret type, and age so we could separate noise from work.
When we dug in, we discovered that just five repositories accounted for roughly 18,000 of those alerts, and every one of those secrets was inactive: test fixtures, deactivated credentials, and fake-but-valid-looking secrets used for testing. (We build secret scanning, so naturally we have repositories full of legitimate-looking secrets in tests.)
For high-volume, low-risk alerts, we developed criteria for bulk closure. If a secret was in a dedicated test repository, had never been active, and matched a known test pattern, we could confidently mark it resolved. In a matter of days, we closed out roughly 18,000 alerts.
The hard questions
We had to make strategic decisions about how to remediate secrets. When a secret lives in an issue, do you edit the body (and potentially remove revision history), or preserve the audit trail? When a secret is committed to a repository, do you rewrite git history? Anyone who’s tried rewriting git history at scale knows what happens next: force-pushes break open pull requests, invalidate commit SHAs, and generally interrupt developers.
A common question was: “Can we just delete the repository if it’s no longer in use?” Our answer was generally no. A deleted repository takes its audit trail with it. If a secret in that repository was ever leaked or the repository was ever compromised, you lose the forensic record you’d need during incident response. Rotate the secret, archive the repository if appropriate, but keep the history.
Whenever possible, we rotate or revoke the exposed secret first. The harder question is whether the residual risk warrants rewriting git history, or whether a revoked secret in history can safely be left in place. These are the types of questions and decisions present with each alert that product security teams wrestle with.
Phase 3: Validate what’s actually live
A credential sitting in a repository might have been rotated years ago, or it might still unlock production systems. You can’t prioritize without knowing the difference.
At the time, secret scanning didn’t have native validity checking, so we built our own approach. The goal was narrow: determine whether a credential still worked and, when appropriate, collect enough metadata to route the alert or notify the right owner.
For example, for a GitHub token, a representative check could make a single authenticated request to a low-impact endpoint like GET /user:
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
Remember, our goal was to answer the smallest useful set of questions: does this credential still work, and who needs to know about it? We treated ambiguous responses as inconclusive, and we avoided follow-on requests to repositories, organizations, or other private resources.
This required close partnership with our privacy and legal teams. Even a “read-only” validity check can have implications when you’re touching a credential you may not own.
As we worked through this manually, our product team built the solution natively, which made the remaining work much faster. Validity checking is now built into GitHub secret scanning.
Phase 4: Figure out who owns what
That cross-functional work also exposed an ownership problem: even after we knew a credential was active, we still had to figure out who could rotate it.
We partnered with customer support, security incident response, and our bug bounty program to develop shared playbooks for secrets reported outside of code. That included redacting secret values before routing work to teams, determining whether a credential belonged to GitHub or a customer, and notifying affected customers or researchers so they could rotate tokens under their control. Across all these workflows, we had to ensure we weren’t creating new problems, like opening issues or pushing commits containing the very secrets we were trying to remediate.
For GitHub-issued credentials like personal access tokens, we worked with our product team to surface secret metadata directly in the alert: who created the token, when, and what scopes it had. That meant we didn’t need to use the token itself to figure out who it belonged to.
For everything else, ownership was harder, and this exposed a deeper problem: not all repositories had clear owners.
Our internal engineering standards (the Engineering Fundamentals program) enforce durable ownership on services, and we maintain a mapping between services and repositories, but not all repositories map cleanly to a service. The pain we experienced led to a broader repository ownership initiative (using GitHub’s Custom Properties), plus a parallel effort to ensure all secrets in our credential manager have durable owners. You can’t rotate a secret if you can’t find the owner.
Phase 5: Manual triage for the long tail
Even with validation and metadata, a long tail of alerts required human judgment. For each one: what does this grant access to, has it been rotated, who owns the connected system, and what’s the remediation path?
For every alert we dismissed, we ensured an accurate disposition (e.g., revoked, used in test, false positive) was recorded, along with a comment containing relevant context, such as a link to a remediation issue or an approved security exception.
This phase required close collaboration across teams to identify system owners, validate remediation status, and assess residual risk where automated signals alone were insufficient.
Phase 6: Systematize and drive accountability
As patterns emerged, we made the work scalable:
We routed alerts into our internal vulnerability management platform for centralized tracking and reporting.
Different credentials need different remediation steps. We documented playbooks by secret type so teams could self-serve.
We automated notifications, routing alerts to the right teams based on repository ownership.
The final piece was accountability. We tied secret remediation to GitHub’s Engineering Fundamentals program, making it a security fundamental that teams were measured against. We set clear expectations and gave teams visibility into status. When secret hygiene is part of how engineering health is measured, it becomes a shared responsibility across the organization.
Nine months after we started, we hit inbox zero.
Lessons learned
Don’t panic at the number. Our initial count was 20,000+ alerts, but 90% were not valid. The raw count is almost never the real scope of work.
Enable and enforce everywhere, no exceptions. Partial rollouts create blind spots. We enabled and enforced secret scanning and push protection at the enterprise level, without allowing anyone to opt out.
Validate before you escalate. Not every detected secret is live. Validation helps you create a prioritized to-do list.
Metadata saves hours. For GitHub credentials, secret metadata cut down the necessary detective work. If you’re working with third-party providers, push them to surface similar metadata, or build your own enrichment layer.
You can’t remediate without ownership. Invest in durable ownership infrastructure early.
Automate the workflow after detection. Detection gets you started, but the operational challenge was routing alerts, tracking owners, and closing the loop. Invest in the workflow layer.
Make it everyone’s problem. Security teams can’t remediate thousands of alerts alone. We tied secret hygiene to our Engineering Fundamentals program. When leadership watches the dashboards, teams find time to fix things.
Document your decision framework. You’ll encounter secrets without clean remediation paths. Document how you decide: When is rotation sufficient? When do you rewrite history? When do you accept residual risk?
What this means for you
You don’t need to reinvent most of what we built. Many of our manual workarounds, including validity checking, ownership identification, and bulk triage, are now native features in secret scanning.
If you’re starting today:
Enable and enforce secret scanning and push protection everywhere.
Triage the backlog by repository and secret type; bulk-close what you can prove is noise.
Validate what’s live before you escalate.
Route alerts to owners, and track remediation like any other engineering work.
Ready to get started? Learn how to enable secret scanning and push protection with GitHub Advanced Security.
Coming soon: How we tackled repository ownership at scale, and why durable ownership of repositories and secrets is the foundation everything else depends on.
The post How GitHub used secret scanning to reach inbox zero appeared first on The GitHub Blog.
関連記事
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み