コンテンツにスキップ

GitHub Secret Scanning API で実現する機密情報監視の自動化

この記事は朝の記事のフォローアップです

朝の記事: GitHubに機密情報を誤って公開した時の完全対処ガイド

ゴール

  • Secret Scanning API を使った定期監視スクリプトの実装
  • 検出時の自動通知システム(Slack/Discord)の構築
  • GitHub Actions での完全自動化と失敗時のリトライ戦略

アーキテクチャ概要

監視システムは以下の3要素で構成:

  1. API Poller: GitHub API で定期的にアラートを取得
  2. Notification Handler: 新規検出を Slack/Discord へ通知
  3. State Manager: 既知アラートの重複通知を防ぐ状態管理

実装ステップ

ステップ1: Python 監視スクリプトの実装

scripts/secret_scanner.py:

import os
import json
import requests
from datetime import datetime
from typing import List, Dict

class SecretScanner:
    def __init__(self, token: str, repo: str):
        self.token = token
        self.repo = repo
        self.api_base = f"https://api.github.com/repos/{repo}"
        self.headers = {
            "Authorization": f"token {token}",
            "Accept": "application/vnd.github.v3+json"
        }
        self.state_file = ".scanner_state.json"

    def get_alerts(self) -> List[Dict]:
        """未解決のシークレットアラートを取得"""
        url = f"{self.api_base}/secret-scanning/alerts"
        params = {"state": "open", "per_page": 100}

        response = requests.get(url, headers=self.headers, params=params)
        if response.status_code == 404:
            return []  # Secret scanning not enabled
        response.raise_for_status()
        return response.json()

    def filter_new_alerts(self, alerts: List[Dict]) -> List[Dict]:
        """新規アラートのみをフィルタリング"""
        known_ids = self.load_state()
        new_alerts = [a for a in alerts if a["number"] not in known_ids]

        # 状態を更新
        if new_alerts:
            for alert in new_alerts:
                known_ids.add(alert["number"])
            self.save_state(known_ids)

        return new_alerts

ステップ2: 通知ハンドラーの実装

class NotificationHandler:
    def __init__(self, webhook_url: str, platform: str = "slack"):
        self.webhook_url = webhook_url
        self.platform = platform

    def send_alert(self, alert: Dict) -> bool:
        """アラートを Slack/Discord に送信"""
        if self.platform == "slack":
            payload = self._format_slack_message(alert)
        else:
            payload = self._format_discord_message(alert)

        response = requests.post(self.webhook_url, json=payload)
        return response.status_code == 200

    def _format_slack_message(self, alert: Dict) -> Dict:
        return {
            "text": f"🚨 機密情報が検出されました",
            "attachments": [{
                "color": "danger",
                "fields": [
                    {"title": "Secret Type", "value": alert["secret_type"], "short": True},
                    {"title": "File", "value": alert["locations"][0]["path"], "short": True},
                    {"title": "Detected", "value": alert["created_at"], "short": False},
                    {"title": "URL", "value": alert["html_url"], "short": False}
                ]
            }]
        }

ステップ3: GitHub Actions ワークフローの設定

.github/workflows/secret-monitoring.yml:

name: Secret Scanning Monitor
on:
  schedule:
    - cron: '*/30 * * * *'  # 30分ごと
  workflow_dispatch:  # 手動実行も可能

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: pip install requests

      - name: Run scanner with retry
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
        run: |
          for i in 1 2 3; do
            python scripts/secret_scanner.py && break
            echo "Attempt $i failed, retrying..."
            sleep 10
          done

ベンチマーク / 比較

監視方法検出速度誤検知率運用コスト
手動チェック24時間以上高(見逃し多)高(人的工数)
Git hooks のみ即座中(ローカル限定)
API 定期監視30分以内低(GitHub 検証済)極低(自動化)
API + Webhooksリアルタイム最低低(初期設定のみ)

失敗パターンと回避策

症状原因回避策
404 エラーSecret scanning 未有効リポジトリ設定で有効化 or graceful handling
Rate limit 超過API 呼び出し過多間隔を 30分以上に調整、条件付きポーリング
重複通知状態管理の不備.scanner_state.json を Actions artifacts で永続化
Webhook 失敗URL 期限切れ/権限不足エラー時の fallback 通知先を設定

自動化の拡張案

  • 優先度判定: secret_type に基づく緊急度の自動分類
  • 自動修正 PR: 検出時に .gitignore 更新 PR を自動作成
  • Issue 作成: 各アラートに対応する GitHub Issue の自動起票
  • メトリクス収集: 検出頻度を Datadog/CloudWatch に送信
  • コンプライアンスレポート: 月次サマリーの自動生成

次のステップ

このガイドを基に、さらなるセキュリティ強化とプロダクション運用の最適化を進めてください。