GitHub secret scanning webhookをSIEMやチケット管理システムへ連携している場合、今回の対応で優先すべきなのは、secret_categoryを受信できるようにすることです。Webhook consumerではdefaultとgenericを許容し、フィールドが存在しない旧ペイロードや将来追加される値も処理できる設計にします。
あわせて、監視対象のsecret typeへapiclub_api_keyとresend_api_keyを追加し、volcengine_ark_api_keyはpush protectionが既定で有効になったことを開発者向け手順へ反映します。GitHubは2026年7月15日、これらの変更とpublic monitoringの表示改善を段階的に展開すると発表しました。(The GitHub Blog)
今回の公式告知はサービス変更の情報提供ですが、独自のWebhook consumerを運用している組織では、厳格なJSONスキーマ、新しいsecret typeを拒否する列挙型、SIEMの分類表などに影響が出る可能性があります。本稿はGitHub.comおよびGitHub Enterprise Cloudのcurrent cloud serviceを対象としています。GitHub Enterprise Serverでは、利用中バージョンのリリースノートを別途確認してください。
GitHub secret scanningの変更点と実務への影響
今回の変更は、Webhookのフィールド追加だけではありません。検出対象、push protection、public monitoringの画面にも変更が含まれます。
| 変更点 | 識別子・対象 | 実務への影響 |
|---|---|---|
| Webhookに分類フィールドを追加 | secret_category | 受信DTO、JSON Schema、データベース、SIEMフィールドを更新する |
| secret categoryを2種類で通知 | default、generic | 自前のsecret type分類表に依存せず、大分類で振り分けられる |
| APIclubの検出を追加 | apiclub_api_key | secret typeの許可リスト、通知名、対応手順へ追加する |
| Resendの検出を追加 | resend_api_key | 分類表とインシデント対応手順へ追加する |
| Resendがsecret scanning partnerに参加 | Resend API key | 公開リポジトリで見つかったキーが発行元へ報告される可能性がある |
| VolcEngine Arkのpush protectionを既定化 | volcengine_ark_api_key | 対象キーを含むpushがブロックされるため、開発者向け案内を更新する |
| public monitoringにinsight cardを追加 | 帰属方法、メンバー数、検証済みドメイン | エンタープライズ全体の漏えい状況を画面上で把握しやすくなる |
新しく追加された検出器はAPIclubとResendです。VolcEngine Arkについては新規検出器の追加ではなく、既存のvolcengine_ark_api_keyがpush protectionの既定対象になった変更として区別する必要があります。(The GitHub Blog)
secret_categoryの意味を正しく理解する
secret_categoryは、アラートの重大度やpush protectionの状態を示すフィールドではありません。検出に使用したパターンを大きく分類するための値です。
| 値 | GitHubでの意味 | 主な利用方法 |
|---|---|---|
default | プロバイダー固有パターンとカスタムパターン | プロバイダー別対応、カスタムルールの確認、標準キューへの振り分け |
generic | 汎用パターンとAIで検出されたシークレット | 内容確認が必要なアラートの振り分け、汎用資格情報の集計 |
unknown | GitHubの公式値ではなく、consumer側で用意する退避値 | フィールド欠落、未知の値、将来の仕様追加を安全に受ける |
GitHub Docsでは、検出パターンをプロバイダー固有、汎用、AI検出などに分類しています。一方、Webhookではプロバイダー固有パターンとカスタムパターンがdefault、汎用パターンとAI検出がgenericにまとめられます。(The GitHub Blog)
defaultは「push protectionが既定」という意味ではない
特に注意したいのが、defaultという名称です。
secret_category: "default"であっても、そのシークレットがpush protectionの既定対象とは限りません。defaultはあくまでパターン分類です。push protectionの対応状況や既定値は、secret typeごとの機能情報として別に管理します。
たとえば、volcengine_ark_api_keyはプロバイダー固有パターンなのでcategoryはdefaultですが、今回の変更でpush protectionも既定対象になりました。一方、同じdefaultに分類される別のsecret typeが、必ずpush protectionでブロックされるとは限りません。
defaultだけではプロバイダーとカスタムパターンを区別できない
defaultには次の両方が含まれます。
- GitHubが提供するプロバイダー固有パターン
- 組織やリポジトリで作成したカスタムパターン
そのため、secret_categoryだけを見てプロバイダーへの連絡やキーの失効処理を自動化するのは危険です。詳細な処理には、従来どおりsecret_typeやプロバイダー情報も使用します。
genericを低優先度に固定しない
genericには、秘密鍵や接続文字列などの汎用パターンに加えて、AIで検出されたパスワードなども含まれます。
誤検知の確認フローを分けることはできますが、genericだから重要度が低いとは限りません。重大度は次の情報を組み合わせて判断します。
- 公開リポジトリか非公開リポジトリか
- シークレットが現在も有効か
- 本番環境へ接続できる資格情報か
- 発見場所がコード、Issue、Pull Request、コメントのどれか
- リポジトリやシステムの重要度
- すでに第三者からアクセスされた形跡があるか
影響が出やすいWebhook consumerの実装
フィールド追加自体は後方互換性を保ちやすい変更です。しかし、consumer側が厳格すぎると受信処理が停止します。
| 既存実装 | 発生しやすい問題 | 修正方針 |
|---|---|---|
additionalProperties: falseを指定したJSON Schema | 未定義のsecret_categoryを受信して検証エラーになる | alertオブジェクトへフィールドを追加し、将来の追加項目も考慮する |
| 未知のプロパティを拒否するデシリアライザー | HTTP 400や500を返す | 境界層では未知フィールドを許容する |
secret_typeをDBのENUMで固定 | APIclubやResendの保存に失敗する | VARCHAR化するか、ENUMへ値を追加する |
| secret typeの分類表をINNER JOINしている | 未登録タイプのイベントが集計から消える | LEFT JOINと未分類値を使用する |
switchのdefaultで例外を投げる | 新しいsecret typeで通知処理が止まる | 未知タイプ用の汎用ルートへ送る |
| categoryを必須列として追加する | 旧fixtureや過去イベントの再送で失敗する | 当初はNULLまたはunknownを許容する |
とくに危険なのは、「未登録のsecret typeを無視する」実装です。consumer自体は正常終了しても、SIEMやダッシュボードにアラートが現れず、監視漏れに気づけないことがあります。
Webhook consumerを更新する手順
secret_categoryを追加可能な値として受け取る
GitHubが現在案内している値はdefaultとgenericです。ただし、受信境界でこの2値だけを厳格な列挙型にすると、将来値が追加された際に再び処理が停止します。
次のように、外部入力はunknownとして受け取り、アプリケーション内部で正規化する方法が安全です。
type NormalizedSecretCategory =
| "default"
| "generic"
| "unknown";
interface SecretScanningAlertWebhook {
action: string;
alert: {
number: number;
secret_type: string;
secret_category?: unknown;
};
repository: {
full_name: string;
};
}
function normalizeSecretCategory(
value: unknown
): NormalizedSecretCategory {
if (value === "default" || value === "generic") {
return value;
}
return "unknown";
}
function buildAlertRecord(
payload: SecretScanningAlertWebhook,
deliveryId: string
) {
const rawCategory =
typeof payload.alert.secret_category === "string"
? payload.alert.secret_category
: null;
return {
deliveryId,
repository: payload.repository.full_name,
alertNumber: payload.alert.number,
action: payload.action,
secretType: payload.alert.secret_type,
secretCategory: normalizeSecretCategory(
payload.alert.secret_category
),
secretCategoryRaw: rawCategory,
};
}
この実装では、次のケースをすべて処理できます。
secret_categoryがdefaultsecret_categoryがgeneric- 旧ペイロードでフィールドが存在しない
nullや不正な型が届く- 将来、GitHubが新しいcategoryを追加する
unknownを受け取った場合はイベントを破棄せず、専用メトリクスを増加させます。たとえば、github_secret_category_unknown_totalのようなカウンターを監視し、値が発生したときだけ仕様変更を確認できるようにします。
生の値と正規化後の値を両方保存する
データベースやログ基盤には、次の2項目を保存すると調査しやすくなります。
| 保存項目 | 内容 |
|---|---|
secret_category | default、generic、unknownに正規化した値 |
secret_category_raw | GitHubから届いた元の文字列 |
将来partnerなどの新しい値が追加された場合でも、unknownとして処理を継続しながら、元の値を確認できます。
ただし、Webhook payload全体や実際のシークレット値をログへ出力してはいけません。監査ログに残す項目は、リポジトリ名、alert number、action、secret type、category、delivery IDなどに限定します。
secret_typeは削除せず、categoryと併用する
secret_categoryが追加されたからといって、既存のsecret type分類表を廃止する必要はありません。両者は用途が異なります。
| 項目 | 適した用途 |
|---|---|
secret_category | 大分類、集計、一次ルーティング、ダッシュボード |
secret_type | 発行元別の対応手順、担当チーム、失効方法、SLA |
| プロバイダー情報 | 発行元への連絡、管理コンソール、契約管理 |
| リポジトリ情報 | システム重要度、担当部署、公開範囲の判定 |
実務では、最初にcategoryで処理キューを分け、その後にsecret typeで具体的な対応手順を選択します。
function selectQueue(
category: NormalizedSecretCategory
): string {
switch (category) {
case "default":
return "secret-provider-or-custom";
case "generic":
return "secret-generic-review";
default:
return "secret-schema-review";
}
}
このルーティング結果だけで重大度を確定させず、リポジトリの公開範囲や資格情報の有効性を後段で評価します。
新しいsecret typeを監視分類表へ追加する
今回反映すべき識別子は次の3つです。
| プロバイダー | secret type | category | 今回の変更 | 監視側で行うこと |
|---|---|---|---|---|
| APIclub | apiclub_api_key | default | 新しい検出器を追加 | 表示名、担当者、失効手順、ダッシュボード分類を追加 |
| Resend | resend_api_key | default | 新しい検出器とpartner対応を追加 | Resend管理者への連絡手順、キー再発行手順を追加 |
| VolcEngine Ark | volcengine_ark_api_key | default | push protectionを既定対象化 | ブロック時の開発者手順、例外申請、再push手順を追加 |
apiclub_api_keyとresend_api_keyは新規検出対象です。ResendはGitHub secret scanning partnerにも加わり、公開リポジトリで露出した対象キーはResendへ転送され、失効や管理者への通知などが行われる場合があります。volcengine_ark_api_keyは今回、新規検出ではなくpush protectionの既定化が行われています。(The GitHub Blog)
分類表で最低限管理する項目
secret typeのマスタには、識別子だけでなく次の情報も持たせます。
- 表示名
- プロバイダー名
- category
- 社内の担当チーム
- インシデント対応SLA
- キーを失効・再発行する管理画面
- 影響確認に使う監査ログ
- push protectionの扱い
- 例外申請の要否
- 最終確認日
新しいsecret typeが届いたとき、単に「GitHub secret」として通知するだけでは、担当者が失効方法を調べるところから始めなければなりません。分類表に対応先まで登録しておくことで、検出から初動までの時間を短縮できます。
未登録のsecret typeも必ず汎用キューへ送る
GitHubでは対応パターンが追加され続けるため、すべての値を事前に登録し続けるのは困難です。
次のような処理は避けます。
if (!secretTypeCatalog[payload.alert.secret_type]) {
return;
}
未登録のsecret typeは破棄せず、汎用キューへ送ります。
const definition =
secretTypeCatalog[payload.alert.secret_type] ?? {
displayName: payload.alert.secret_type,
owner: "security-operations",
playbook: "generic-provider-secret",
};
この設計なら、分類表の更新が遅れても検出自体は失われません。
VolcEngine Arkのpush protection既定化で変わること
volcengine_ark_api_keyは、secret scanningが有効なリポジトリでpush protectionの既定対象になりました。無料の公開リポジトリも含め、対象キーを含むコミットは自動的にブロックされます。(The GitHub Blog)
開発者向け手順を更新する
pushがブロックされたときの手順には、少なくとも次の内容を入れます。
- コードや設定ファイルからキーを削除する
- コミット履歴やステージング対象にキーが残っていないか確認する
- 環境変数やsecret managerへ移す
- 実際に使用中のキーなら、露出範囲を確認して必要に応じてローテーションする
- テスト用の文字列なら、実キーに似ないダミー値へ置き換える
- bypassが必要な場合は、理由、承認者、期限を記録する
- 修正後に再度pushする
検証のために本物のVolcEngine Ark keyをテストリポジトリへ登録するのは避けてください。テストでは、GitHubのRecent deliveriesから取得した値をマスクしたfixtureや、フィールド構造だけを再現したサンプルを使います。
categoryからpush protectionを推測しない
volcengine_ark_api_keyのcategoryはプロバイダー固有パターンとしてdefaultになります。しかし、push protectionが既定で有効になったことはcategoryとは別の変更です。
したがって、次のような判定は誤りです。
const pushProtectionEnabled =
secretCategory === "default";
push protectionの状態はsecret typeごとのメタデータとして管理するか、GitHubの最新の対応パターン情報を参照して判断します。
Public monitoringの表示変更で確認すること
public monitoringのアラート一覧上部には、次のinsight cardが表示されるようになりました。
member activityとverified domainによる漏えい件数の内訳- エンタープライズのメンバー数
- エンタープライズ所有およびOrganization所有の検証済みドメイン
これはpublic monitoring画面の情報追加であり、secret_scanning_alert webhookへ同じ情報が追加されたという意味ではありません。Webhookのスキーマ変更と、管理画面の表示変更は分けて扱います。(The GitHub Blog)
public monitoringは、エンタープライズ所有外の公開リポジトリも対象とし、IssueやPull Requestのコメントなど、コード以外の公開コンテンツも監視します。漏えいの帰属には、エンタープライズメンバーによる活動と、検証済みドメインに一致するメールアドレスが使われます。(GitHub Docs)
管理者はinsight cardを確認し、次の基準値を記録しておくと変化を検知しやすくなります。
member activity由来の現在のアラート件数verified domain由来の現在のアラート件数- エンタープライズメンバー数
- 登録されている検証済みドメイン
- 未対応アラートの件数と最古の発生日
verified domain由来のアラートが増えた場合は、エンタープライズへ直接参加していない開発者、委託先、退職者、個人アカウントなどが会社ドメインのメールアドレスを利用していないか確認します。
なお、public monitoringは現在public previewで、GitHub Advanced SecurityまたはGitHub Secret Protectionが有効なGitHub Enterprise Cloudで利用できます。GitHub Enterprise Cloud with data residencyでは利用できないと案内されています。(GitHub Docs)
Webhookの安全性と処理方式も同時に見直す
スキーマ更新だけでなく、Webhook consumerの基本的な保護も確認します。
署名検証は生のリクエスト本文で行う
GitHub webhookは、X-Hub-Signature-256を使ってHMAC-SHA256署名を検証します。JSONをパース、整形、文字コード変換した後ではなく、受信した生の本文を使って検証します。比較にはタイミング攻撃を避けられる定数時間比較を使用します。(GitHub Docs)
処理順序は次のようにします。
- 生のリクエスト本文を取得する
X-Hub-Signature-256を検証するX-GitHub-Eventがsecret_scanning_alertか確認する- JSONを解析する
- top-levelの
actionを確認する - キューへ登録する
- 2XXを返す
- SIEM、Slack、チケットシステムへの連携を非同期で処理する
10秒以内に2XXを返す
GitHubはWebhook受信側に10秒以内の2XX応答を推奨しています。SIEMやチケットシステムの応答を同期的に待つと、外部サービスの遅延によってWebhook deliveryが失敗します。署名検証とキュー登録までを同期処理とし、それ以降を非同期化します。(GitHub Docs)
X-GitHub-Deliveryで重複処理を防ぐ
X-GitHub-Deliveryを一意キーとして保存し、同じdelivery IDを受信した場合は二重にチケットを発行しないようにします。
再送を実行した場合もdelivery IDは元の配信と同じです。単純に「既処理なら無視」するだけでなく、前回が途中失敗だった場合に再処理できる状態管理が必要です。(GitHub Docs)
たとえば、状態を次のように分けます。
receivedqueuedprocessingcompletedfailed
completedなら重複処理を止め、failedなら再送時に再処理できる設計にします。
更新前に実施するテスト
本番へ反映する前に、最低限次のテストケースを用意します。
| テストケース | 入力例 | 期待結果 |
|---|---|---|
| default category | "secret_category": "default" | 標準キューへ送られる |
| generic category | "secret_category": "generic" | generic確認キューへ送られる |
| フィールドなし | secret_categoryを省略 | unknownとして処理を継続する |
| 将来の未知値 | "secret_category": "future_value" | 4XXや500を返さず、未知値を記録する |
| APIclub | "secret_type": "apiclub_api_key" | APIclubとして保存・通知される |
| Resend | "secret_type": "resend_api_key" | Resendの対応手順が選ばれる |
| VolcEngine Ark | "secret_type": "volcengine_ark_api_key" | VolcEngineの分類とpush protection情報が表示される |
| 未登録secret type | 任意の未知値 | 汎用キューへ送られる |
| 不正な署名 | 誤った署名 | 処理せず認証エラーを返す |
| 重複delivery | 同じX-GitHub-Delivery | チケットを二重作成しない |
| 下流サービス停止 | SIEMやSlackがタイムアウト | Webhook受信は2XX、キューから再試行する |
Recent deliveriesを使って確認する
GitHubのWebhook設定画面では、Recent deliveriesから実際のヘッダーとペイロードを確認できます。直近3日間のdeliveryは再送できるため、consumer更新後の結合テストにも利用できます。
ただし、GitHubは失敗したWebhookを自動では再送しません。障害中に失敗したdeliveryは、画面またはAPIから明示的に再送する必要があります。(GitHub Docs)
再送テストでは、同じdelivery IDによってイベントが重複登録されないことも確認します。
よくある失敗と回避方法
defaultとgenericだけの厳格なENUMにする
アプリケーション内部の正規化後データとしてENUMを使うのは問題ありません。しかし、Webhookの受信境界で未知値を拒否すると、将来のフィールド拡張で再び停止します。
外部入力は文字列として受け、内部でunknownへ変換する構成にします。
categoryだけを保存し、secret typeを捨てる
categoryは集計には便利ですが、APIclub、Resend、VolcEngineなどの発行元別対応には使えません。secret_typeとsecret_categoryの両方を保存します。
新しいsecret typeを通知名だけに追加する
表示名を追加しても、担当者、失効手順、SLA、ダッシュボード分類が未登録では運用できません。分類表とインシデント対応手順を一緒に更新します。
未登録タイプをINNER JOINで除外する
イベントテーブルとsecret typeマスタをINNER JOINすると、マスタ未登録の新しいsecret typeがレポートから消えます。LEFT JOINを使い、未登録を「Unknown secret type」として可視化します。
Webhook payloadをそのままログへ出す
secret scanningのペイロードをデバッグ目的で丸ごと出力すると、セキュリティ情報やリポジトリ情報がログ基盤へ広がります。許可したメタデータだけを構造化ログとして記録します。
secret_scanning_alert_locationにも同じ変更があると決めつける
GitHubでは、secret scanning alert本体のイベントと、検出場所に関するsecret_scanning_alert_locationは別イベントです。今回明示されている対象はsecret_scanning_alertなので、locationイベントにも同じフィールドがあると仮定せず、実際のペイロードと公式スキーマを確認します。(GitHub Docs)
対応の優先順位
今回の変更に追従する際は、次の順番で進めると監視漏れを防げます。
- Recent deliveriesから現在のペイロードを取得し、機密値を除いてfixture化する
alert.secret_categoryを受信モデルへ追加するdefault、generic、欠落、未知値を処理できるようにするsecret_typeとsecret_categoryを両方保存するapiclub_api_keyとresend_api_keyを監視分類表へ追加するvolcengine_ark_api_keyのpush protection既定化を開発者向け手順へ反映する- 未登録secret typeが汎用キューへ送られることを確認する
- 署名検証、10秒以内の応答、delivery IDによる重複防止をテストする
- 段階的にリリースし、
unknowncategoryと未登録secret typeの件数を監視する - public monitoringのinsight cardを確認し、現在値を基準として記録する
最優先は、受信境界を追加フィールドと未知値に強くすることです。そのうえで、新しいsecret typeの分類表、VolcEngine Arkのpush protection対応、public monitoringの確認手順を更新すれば、今回の変更だけでなく今後の検出器追加にも追従しやすい監視基盤になります。

コメント