Secret Scanning with GitHub Advanced Security: Preventing Credential Leaks

Secret Scanning with GitHub Advanced Security

Secret scanning GitHub Advanced Security detects credentials, API keys, tokens, and other secrets accidentally committed to repositories. In 2026, with over 200 supported secret types and push protection that blocks commits containing secrets before they reach the repository, this feature has become an essential layer of defense against credential leaks that lead to data breaches.

This guide covers implementing comprehensive secret scanning across your organization, including push protection configuration, custom secret patterns, incident response procedures, and integration with secret management tools like HashiCorp Vault and AWS Secrets Manager.

The Cost of Leaked Secrets

A single leaked AWS access key can result in hundreds of thousands of dollars in unauthorized charges within hours. Moreover, leaked database credentials expose customer data, triggering compliance violations and breach notification requirements. GitHub reports that over 10 million secrets are leaked in public repositories annually — and private repositories are equally at risk from insiders and compromised developer machines.

Secret scanning GitHub credential leak prevention
Secret scanning catches credentials before they become a breach vector
Common Secret Types Detected

┌────────────────────────────┬────────────┬──────────────┐
│ Secret Type                │ Severity   │ Detection    │
├────────────────────────────┼────────────┼──────────────┤
│ AWS Access Keys            │ Critical   │ Built-in     │
│ Azure Service Principal    │ Critical   │ Built-in     │
│ GCP Service Account Key    │ Critical   │ Built-in     │
│ GitHub PAT/OAuth Token     │ High       │ Built-in     │
│ Database Connection String │ Critical   │ Built-in     │
│ Private SSH Keys           │ High       │ Built-in     │
│ Stripe API Keys            │ High       │ Built-in     │
│ Slack Webhook URLs         │ Medium     │ Built-in     │
│ JWT Signing Keys           │ High       │ Custom       │
│ Internal API Keys          │ Medium     │ Custom       │
│ Encryption Keys            │ Critical   │ Custom       │
└────────────────────────────┴────────────┴──────────────┘

200+ built-in patterns + custom patterns

Enabling Secret Scanning

# Enable for a single repository via API
gh api repos/{owner}/{repo} \
  --method PATCH \
  --field security_and_analysis.secret_scanning.status=enabled \
  --field security_and_analysis.secret_scanning_push_protection.status=enabled

# Enable for entire organization
gh api orgs/{org} \
  --method PATCH \
  --field security_and_analysis.secret_scanning.status=enabled \
  --field security_and_analysis.secret_scanning_push_protection.status=enabled

Push Protection Configuration

Push protection is the most valuable feature — it blocks commits containing secrets before they are pushed. Therefore, secrets never reach the repository and never appear in git history:

# .github/secret_scanning.yml — Organization-level config
paths-ignore:
  - 'test/fixtures/**'
  - '**/*.test.js'
  - 'docs/examples/**'

# Custom patterns for internal secrets
patterns:
  - name: Internal API Key
    secret_type: internal_api_key
    pattern: 'MYAPP_KEY_[A-Za-z0-9]{32}'

  - name: Internal JWT Secret
    secret_type: jwt_secret
    pattern: 'JWT_SECRET=[A-Za-z0-9+/=]{44,}'

  - name: Database URL
    secret_type: database_url
    pattern: '(postgres|mysql|mongodb)://[^\s]+:[^\s]+@[^\s]+'

Custom Secret Patterns

Organizations often have internal credential formats that built-in patterns do not cover. Additionally, custom patterns let you detect company-specific secrets and configuration values:

# Create custom pattern via API
gh api orgs/{org}/secret-scanning/custom-patterns \
  --method POST \
  --field name="Internal Service Token" \
  --field pattern="svc_tok_[a-f0-9]{40}" \
  --field description="Internal microservice authentication token" \
  --field severity="high"

# Create pattern with test strings for validation
gh api orgs/{org}/secret-scanning/custom-patterns \
  --method POST \
  --field name="Encryption Key" \
  --field pattern="ENC_KEY_[A-Za-z0-9]{64}" \
  --field description="Application encryption key" \
  --field severity="critical" \
  --field test_strings='["ENC_KEY_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"]'

Incident Response Workflow

When a secret is detected, speed matters. Consequently, automate the response process to minimize the exposure window:

import requests
import json
from datetime import datetime

class SecretIncidentHandler:
    def __init__(self, github_token, vault_url, slack_webhook):
        self.headers = {
            'Authorization': f'token {github_token}',
            'Accept': 'application/vnd.github+json',
        }
        self.vault_url = vault_url
        self.slack_webhook = slack_webhook

    def handle_alert(self, alert):
        """Process a secret scanning alert."""
        secret_type = alert['secret_type']
        severity = self.classify_severity(secret_type)

        # Step 1: Rotate the compromised credential
        if secret_type.startswith('aws_'):
            self.rotate_aws_key(alert)
        elif secret_type == 'database_url':
            self.rotate_db_credential(alert)

        # Step 2: Alert the team
        self.notify_slack(alert, severity)

        # Step 3: Resolve the alert
        self.resolve_alert(alert['number'], 'revoked')

        # Step 4: Log for compliance
        self.log_incident(alert, severity)

    def rotate_aws_key(self, alert):
        """Automatically rotate compromised AWS keys."""
        import boto3
        iam = boto3.client('iam')

        # Deactivate the leaked key immediately
        access_key = alert['secret'][:20]  # AKIA...
        iam.update_access_key(
            AccessKeyId=access_key,
            Status='Inactive'
        )

        # Create new key and store in Vault
        new_key = iam.create_access_key(
            UserName=alert.get('push_protection_bypassed_by',
                              {}).get('login', 'unknown')
        )
        self.store_in_vault(
            f"aws/{alert['repository']['name']}",
            new_key['AccessKey']
        )

    def notify_slack(self, alert, severity):
        """Send incident notification to security channel."""
        color = {'critical': '#ff0000', 'high': '#ff8800',
                 'medium': '#ffcc00'}.get(severity, '#cccccc')

        requests.post(self.slack_webhook, json={
            'attachments': [{
                'color': color,
                'title': f'Secret Leaked: {alert["secret_type"]}',
                'fields': [
                    {'title': 'Repository',
                     'value': alert['repository']['full_name']},
                    {'title': 'Severity', 'value': severity},
                    {'title': 'Committer',
                     'value': alert.get('push_protection_bypassed_by',
                              {}).get('login', 'Unknown')},
                ],
            }]
        })
Secret scanning incident response automation
Automated incident response minimizes the window of exposure for leaked credentials

Pre-Commit Prevention

The best approach is preventing secrets from being committed in the first place. Furthermore, pre-commit hooks provide a local safety net before code reaches GitHub:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks
        name: Detect secrets with Gitleaks
        entry: gitleaks protect --staged --verbose
        language: golang

  - repo: https://github.com/trufflesecurity/trufflehog
    rev: v3.63.0
    hooks:
      - id: trufflehog
        name: TruffleHog secret scan
        entry: trufflehog git file://. --only-verified --fail
# .gitleaks.toml — Custom rules
[extend]
useDefault = true

[[rules]]
id = "internal-api-key"
description = "Internal API key detected"
regex = '''MYAPP_KEY_[A-Za-z0-9]{32}'''
secretGroup = 0

[[rules]]
id = "env-file-secret"
description = "Secret in environment variable assignment"
regex = '''(?i)(password|secret|api_key|token)s*=s*['"][^'"]{8,}['"]'''
secretGroup = 0

[rules.allowlist]
paths = [
  '''.example$''',
  '''.sample$''',
  '''test/''',
]

When NOT to Use GitHub Secret Scanning Alone

GitHub secret scanning only covers repositories hosted on GitHub. If your organization uses GitLab, Bitbucket, or self-hosted Git, you need additional tools like Gitleaks or TruffleHog. Additionally, secret scanning does not cover runtime environments — secrets hardcoded in deployed containers, environment variables, or CI/CD logs require separate monitoring. Secret scanning is a detection tool, not a prevention framework — it should be part of a broader secrets management strategy that includes vault-based secret storage, short-lived credentials, and workload identity federation.

Comprehensive secrets management architecture
Secret scanning is one layer in a comprehensive secrets management strategy

Key Takeaways

  • Secret scanning GitHub Advanced Security detects 200+ credential types with push protection blocking commits before they reach the repo
  • Custom patterns extend detection to organization-specific secrets and internal credential formats
  • Automated incident response with credential rotation minimizes the exposure window after a leak
  • Pre-commit hooks with Gitleaks provide local prevention before secrets leave the developer machine
  • Combine with vault-based secret storage and workload identity for a complete secrets management strategy

Related Reading

External Resources

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top