Topic: mcp server secret scanning security
MCP server secret scanning security — detecting hardcoded credentials, pre-commit hooks, and git history remediation
MCP servers are typically developed quickly and published to public GitHub repositories early in their lifecycle. The same speed that makes the MCP ecosystem vibrant creates a credential leak pattern that SkillAudit's scan data confirms: approximately 40% of community MCP servers contain at least one hardcoded credential in their git history, even if that credential was removed from the current HEAD. A secret committed to git — even in a commit that was later deleted — persists in reflog, fork clones, and automated harvest bots that scrape public repositories. Secret scanning must happen before the commit, not after it is public.
The secret leak lifecycle in MCP server development
The typical credential leak follows a predictable pattern:
- Development shortcut. The author pastes a real API key into
config.jsor.env.exampleto get the server running locally, intending to replace it before pushing. - Accidental commit.
git add .captures the file. The commit goes to the public repo. - Delayed discovery. The author notices (or is notified by GitHub's push protection or a security researcher) and removes the key in a follow-up commit.
- False sense of resolution. The key is gone from HEAD, but git history is permanent. The key is in every clone and fork made between step 2 and step 4.
- Parallel harvesting. Automated bots that watch GitHub's event stream for credential patterns (Gitleaks, TruffleHog, specialized credential harvesters) processed the repository within minutes of step 2. The key was likely already tested and potentially used.
The window between commit and revocation — during which the credential is live and public — is typically measured in hours or days. The right mitigation is not faster revocation; it is preventing the commit from happening in the first place.
The most common credential types in MCP server repos
SkillAudit's credential exposure scan identifies these credential types in community MCP servers most frequently:
- OpenAI / Anthropic API keys (
sk-prefix,sk-ant-prefix) — found in tool handlers that call LLM APIs directly. - Stripe secret keys (
sk_live_,sk_test_) — found in payment and subscription management tools. - Database connection strings (
postgresql://user:password@host,mongodb://user:password@host) — found in hardcoded fallback connection strings when environment variable loading fails. - GitHub personal access tokens (
ghp_,github_pat_) — found in tools that read or write GitHub repositories. - AWS access key IDs and secrets (
AKIAprefix) — found in tools that interact with S3, Lambda, or other AWS services. - JWT signing secrets — short alphanumeric strings assigned to
JWT_SECRETvariables in source files.
Fix 1 — pre-commit secret detection with detect-secrets
detect-secrets (Yelp's open-source tool) scans staged content for high-confidence secret patterns before the commit is finalized. Installing it as a pre-commit hook blocks the commit if any patterns match:
# Install detect-secrets
pip install detect-secrets
# Generate a baseline of known false positives in the repo
detect-secrets scan --baseline .secrets.baseline
# .pre-commit-config.yaml
repos:
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets.baseline']
# Install the hooks
pre-commit install
# Now every git commit runs detect-secrets on staged files.
# If a new secret is found that isn't in the baseline, the commit is blocked:
#
# Detect secrets...................................................Failed
# - hook id: detect-secrets
# - exit code: 1
# ERROR: Potential secrets found.
# Secret Type: Secret Keyword
# Location: src/config.js:12 (stripe_secret_key)
Fix 2 — git-secrets for AWS and common credential patterns
git-secrets (AWS Labs) focuses on AWS credential patterns and integrates directly as a git hook rather than through the pre-commit framework:
# Install git-secrets
brew install git-secrets # macOS
# or build from source on Linux
# Register AWS patterns (built-in)
git secrets --register-aws
# Add custom patterns for other credential types
git secrets --add 'sk_live_[a-zA-Z0-9]{24}' # Stripe live keys
git secrets --add 'sk-ant-[a-zA-Z0-9\-]{95}' # Anthropic keys
git secrets --add 'ghp_[a-zA-Z0-9]{36}' # GitHub PATs
git secrets --add 'postgresql://[^:]+:[^@]+' # Postgres connection strings
# Install as a hook in the current repo
git secrets --install
# Or install globally for all new repos
git secrets --install ~/.git-templates/git-secrets
git config --global init.templateDir ~/.git-templates/git-secrets
Fix 3 — CI pipeline secret scanning with TruffleHog
Pre-commit hooks rely on developers having them installed. CI-level scanning catches secrets that slip through on developer machines where hooks were not installed, or in commits made through the GitHub web UI:
# GitHub Actions workflow: scan every pull request for secrets
name: Secret Scan
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
trufflehog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history — TruffleHog scans all commits
- name: TruffleHog Secret Scan
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
extra_args: --only-verified # Only alert on verified (active) credentials
--only-verified makes TruffleHog test the detected credential against the issuing API before alerting — this dramatically reduces false positives and ensures alerts represent real, currently-active leaked credentials.
Fix 4 — remediation when a secret is already in git history
If a credential is already in the repository's git history, removing it from HEAD is not sufficient. The credential must be revoked immediately (rotation, deletion, or scope restriction at the issuing service) and the git history must be rewritten:
# Step 1: Revoke the credential immediately — do this FIRST before any git operations
# (git history rewrite is slower than credential compromise)
# Step 2: Rewrite git history using git filter-repo (preferred over BFG)
pip install git-filter-repo
# Remove a specific file from all commits
git filter-repo --path config.js --invert-paths
# Or replace all occurrences of the secret string
git filter-repo --replace-text <(echo 'ACTUAL_SECRET_VALUE==>REDACTED')
# Step 3: Force-push to remote (requires branch protection bypass — coordinate with team)
git push origin main --force
# Step 4: Notify all existing forks — GitHub's "delete fork network" for critical leaks
# Step 5: Rotate any credentials that were in those forks
SkillAudit detection
SkillAudit scans both the current HEAD and the full commit history (for public repositories) for credential patterns. The Credential exposure axis includes:
- Pattern matching for 80+ known credential formats (API key prefixes, connection string patterns, token formats) in all source files and configuration files.
- Historical scan: credential patterns found in any commit in the accessible git history are flagged even if removed from HEAD — the historical exposure is scored separately from the current HEAD exposure.
- Entropy analysis for high-entropy strings assigned to variable names associated with secrets (
SECRET,KEY,TOKEN,PASSWORD,CREDENTIAL). - False positive reduction: patterns that match test fixtures, example files, or
secrets.baselineentries are not flagged.
The Credential exposure score is one of the highest-weight axes in the SkillAudit overall grade because it is the most immediately actionable risk: a detected secret can be revoked and rotated immediately, unlike architectural flaws that require refactoring. See the credential leak anatomy post for the full taxonomy of credential exposure patterns that SkillAudit detects, including runtime leaks (credentials echoed in logs or tool responses) in addition to the static code patterns covered here.