Skip to content
ExponaBLOG
GIT & GITHUB FOR PROFESSIONAL DEVELOPERS · DAY 048 / 100

Secret scanning on GitHub: practical tips and trade-offs

Catch leaks before they spread.

Expona Admin7 min read

Every production incident we've seen involving leaked credentials started the same way: a developer pushed a database password, API key, or private token to a public repository, and within hours, automated bots were already harvesting it. Secret scanning on GitHub: practical tips and trade-offs is not optional if you're running a professional team—it's the difference between catching a leak during code review and discovering it in your logs three months later. In this article, we'll walk through how to set up scanning, configure it for your stack, and handle the real-world complexity of secrets in motion.

Enable secret scanning and push protection

GitHub secret scanning works in two modes: repository scanning, which examines your history and new commits for known patterns (AWS keys, GitHub tokens, Stripe keys, and dozens more), and push protection, which blocks commits containing detectable secrets before they hit your repository at all.

For a mid-sized SaaS team, start by enabling both on every repository. On GitHub.com, navigate to your repository settings → Security → Secret scanning. You'll see two toggles:

  • Secret scanning — scans commits as they're pushed and on-demand scans of your history
  • Push protection — actively prevents commits with high-confidence secrets from being pushed to any branch

Push protection is the more aggressive choice. When enabled, a developer attempting to commit a hardcoded AWS access key will see an immediate rejection:

bash
$ git push origin feature/auth
remote: error: push blocked by GitHub secret scanning push protection
remote: An AWS Access Key ID was detected in your changes.
remote: Visit https://github.com/your-org/repo/security/secret-scanning to review and manage the alert.
To github.com:your-org/repo.git
 ! [remote rejected] feature/auth → feature/auth (push blocked by secret scanning)
error: failed to push some refs to 'github.com:your-org/repo.git'

This is intentional friction. The developer must either remove the secret, rotate it (if it was already committed), or dismiss the alert with a justification if it's a false positive. For a team moving fast, the first few days of push protection can feel noisy—expect developers to ask, "Can't we just disable this?" The answer is no, but you can tune it.

At the organization level (if you're on GitHub Enterprise or a team plan), enforce secret scanning and push protection as a policy so no repository can accidentally disable it. This prevents the common scenario where a team lead turns it off for "speed" and never turns it back on.

Custom patterns for internal tokens

GitHub's built-in patterns catch the big players—Stripe, Twilio, AWS—but they miss your internal services. If your team generates JWT tokens, API keys for internal GraphQL endpoints, or session tokens with a specific prefix, GitHub has no idea they're secrets. This is where custom patterns shine.

Let's say your internal payment service issues access tokens that always start with internal_pay_ and are 40 characters of alphanumeric. Under repository settings → Secret scanning, add a custom pattern:

Field Value
Name Internal Payment Service Token
Pattern internal_pay_[a-zA-Z0-9]{40}
Start pattern internal_pay_

The "start pattern" is crucial—it's a substring that GitHub uses to narrow down where to search in your files before it runs the full regex. This dramatically speeds up scanning, especially on large repositories with gigabytes of history.

Test your pattern in the UI with a sample token before enabling it. A too-broad pattern (like [a-z]{10,}) will generate thousands of false positives and crater team morale. A too-narrow pattern will miss real secrets. Aim for a pattern specific enough that accidental matches are rare but flexible enough to catch legitimate variants.

Custom patterns apply at the organization level in GitHub Enterprise. If you're on a free or pro plan, they're per-repository. Document them in your security runbook so when new repositories are created, whoever sets them up knows to copy the custom patterns over.

Alerts go to security owners

A secret scanning alert sitting in a repository with 50 developers isn't an alert—it's background noise. Alerts must route to people who own the response.

GitHub secret scanning alerts can be configured to notify specific teams or individuals. In your repository settings under Secret scanning, configure who gets notified. On a mature team, this is usually:

  • A dedicated @security-team GitHub team who owns the initial triage
  • The repository's code owners (for context on what the secret is for)
  • A Slack webhook that posts to a #security-alerts channel

The Slack integration is particularly valuable because it gets the alert into the chat stream where engineers are already hanging out. A GitHub notification sitting in their inbox for days is useless; a Slack message in a channel they monitor gets handled in minutes.

Set up the webhook using GitHub Actions or a native Slack integration. Here's a minimal workflow that notifies Slack when a new secret is detected:

bash
name: Notify on Secret Alert
on:
  repository_dispatch:
    types: [secret_scanning_alert]
jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - name: Send Slack notification
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Secret scanning alert in ${{ github.repository }}",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*Secret Detected*\nRepository: ${{ github.repository }}\nAlert: ${{ github.event.client_payload.alert_type }}"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

The real tension here is response time. If rotating a leaked AWS key takes 48 hours because the alert got lost in email, that's a failure of your process, not your scanning tool. Define a response SLA—high-confidence secrets (actual AWS keys, database passwords) should be reviewed and acted on within 2 hours. False positives and test credentials can have a longer window.

Rotate any detected secret

This is the part teams often get wrong. A secret found by scanning doesn't just need to be removed from your codebase—it needs to be rotated in the system it protects. If GitHub detects your production database password in a commit, removing it from Git doesn't invalidate it in your actual database.

Here's a real scenario: a developer accidentally commits a .env file with their local RDS password. Secret scanning catches it immediately and alerts your security team. The team removes the file, but the password is still valid. An attacker who cloned the repository before the alert can still connect to your database.

Your response checklist should be:

  1. Alert fires → confirmed in #security-alerts Slack channel
  2. Triage: is this a real secret or a false positive?
  3. If real: immediately revoke/rotate the secret in the system it protects (RDS, API gateway, service account, etc.)
  4. Force-push or rewrite Git history to remove the commit (or document why you can't)
  5. Investigate: who has access to the repository? When was the secret added? Could it have been cloned elsewhere?
  6. Post-incident: update your secrets management (environment variables, vaults, secrets manager) so this specific secret never lands in code again

Many teams skip step 3 and call it handled. This is a critical gap. If you discover a leaked production key on Friday afternoon, you have a small window before an attacker starts using it. Rotation must happen within minutes, not hours.

Automate what you can. If a GitHub Actions secret is accidentally committed, you can revoke it programmatically. If it's a database password, you need manual intervention or a secrets management system (like AWS Secrets Manager) that can automatically rotate credentials. The goal is to close the window between discovery and invalidation to as close to zero as possible.

Common mistakes

  • Disabling push protection because it's "too strict." It's not strict; developers need to learn not to commit secrets. The temporary friction is the point. After a few weeks, it becomes habit.
  • Treating false positives the same as real leaks. GitHub lets you mark alerts as false positives, but it shouldn't be a dismissal button to make noise go away. Each false positive should trigger a review of your custom patterns or documentation.
  • Rotating the secret in Git but not in the actual service. This leaves you exposed. Always rotate both.
  • Not checking Git history before enabling secret scanning. If you scan an old repository for the first time, it'll surface secrets from years ago that are already rotated. This is fine and expected, but your team needs to expect that volume initially.
  • Assuming no one will think to clone a public repository containing secrets. They will, within minutes. Rotation urgency should be high.

Key takeaways

  • Enable both secret scanning and push protection on every repository. Push protection prevents leaks at commit time rather than after they've spread.
  • Define custom patterns for any token or credential your team generates internally. Use a specific start pattern to keep scanning fast.
  • Route alerts to a dedicated security channel or team, not an inbox. Response time matters—secrets left in the wild are exploited within hours.
  • Rotation is two-part: remove the secret from Git history and revoke it in the system it protects. Both must happen, and both must happen quickly.
  • Expect initial noise and false positives. Tune your patterns and documentation to reduce them, but don't disable the tool to make the alerts stop.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles