Bonaventure OgetoBy Bonaventure Ogeto|

Never Commit Your Paystack Key: Detection and Remediation

Add a pre-commit hook using git-secrets or gitleaks that scans for sk_test_ and sk_live_ patterns before allowing a commit. If a key has already been committed, rotate it immediately on the Paystack dashboard, then remove it from the git history using git filter-repo. Enable GitHub secret scanning for ongoing protection.

Why a Committed Key Is a Leaked Key

When you commit a Paystack secret key to a git repository, it stays in the history even after you delete the file. Anyone with read access to that repository can find it. If the repository is public, automated bots scan GitHub, GitLab, and Bitbucket continuously for patterns like sk_live_ and exploit them within minutes.

Even private repositories are risky. Team members come and go. Repositories get forked. Backup services copy your repo. A key that was "safe" in a private repo today might be exposed tomorrow through any of these channels.

The rule is simple: if a Paystack secret key has ever been in a git commit, treat it as compromised and rotate it. There is no safe way to "un-commit" a key and trust that nobody saw it.

Detecting Keys Already in Your Repository

Before setting up prevention, check whether your repository already contains committed keys. Here are three ways to scan your git history:

Manual Search with Git Log

git log -p --all -S 'sk_live_' -- .
git log -p --all -S 'sk_test_' -- .

This searches the diff of every commit across all branches for the pattern. If it returns results, you have a committed key.

Using gitleaks

Gitleaks is a purpose-built tool for finding secrets in git repositories:

brew install gitleaks
gitleaks detect --source . --verbose

Gitleaks comes with built-in rules for Paystack key patterns, AWS keys, database URLs, and dozens of other secret types. It scans the full git history by default.

Using truffleHog

pip install trufflehog
trufflehog git file://. --only-verified

TruffleHog scans for high-entropy strings and known secret patterns. The --only-verified flag reduces false positives by checking if discovered credentials are actually valid.

If any of these tools find a Paystack key in your history, follow the remediation steps in the sections below. But the first thing you do, before cleaning up the history, is rotate the key on the Paystack dashboard.

Setting Up Pre-Commit Hooks

Pre-commit hooks run before every commit and can reject the commit if they find a secret. This is the most effective prevention because it stops the key from ever entering the repository.

Using git-secrets (AWS tool, works for any pattern)

# Install
brew install git-secrets

# Set up in your repository
cd /path/to/your/project
git secrets --install

# Add Paystack patterns
git secrets --add 'sk_test_[a-zA-Z0-9]+'
git secrets --add 'sk_live_[a-zA-Z0-9]+'

# Optional: also catch webhook secrets
git secrets --add 'whsk_[a-zA-Z0-9]+'

Now any git commit that includes a matching pattern will be rejected with an error message.

Using gitleaks as a pre-commit hook

# Install the pre-commit framework
pip install pre-commit

Create a .pre-commit-config.yaml in your project root:

repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks
# Install the hook
pre-commit install

Gitleaks already knows about Paystack key patterns. Any commit containing sk_test_ or sk_live_ will be blocked automatically.

Using Husky (Node.js projects)

If your project already uses Husky for linting or formatting hooks, you can add a secret scan:

npx husky add .husky/pre-commit "npx gitleaks protect --staged --verbose"

This scans only staged files, so it runs fast even in large repositories.

GitHub Secret Scanning

GitHub automatically scans public repositories for known secret patterns, including Paystack API keys. If it finds a match, it can notify you and, in some cases, notify Paystack directly to revoke the key.

For private repositories, GitHub secret scanning is available on GitHub Advanced Security (paid feature) or on all repositories for free with push protection enabled.

To enable push protection:

  1. Go to your repository Settings
  2. Navigate to Code security and analysis
  3. Enable "Secret scanning" and "Push protection"

With push protection enabled, GitHub will block a push that contains a detected secret. The developer sees an error message explaining which secret was found and must remove it before pushing again.

This is a safety net, not a replacement for pre-commit hooks. By the time GitHub detects the secret, the key is already in your local git history. Pre-commit hooks catch it earlier. Use both.

Removing Keys from Git History

Deleting a file that contains a key and committing the deletion does not remove the key from history. The key is still visible in the diff of the commit that added it. You need to rewrite history.

Using git filter-repo (recommended)

# Install
pip install git-filter-repo

# Remove a specific file from all history
git filter-repo --path .env --invert-paths

# Or replace a specific string across all history
git filter-repo --replace-text expressions.txt

Where expressions.txt contains lines like:

sk_live_xxxxxxxxxxxxxxxxxxxx==>REDACTED
sk_test_xxxxxxxxxxxxxxxxxxxx==>REDACTED

Using BFG Repo-Cleaner

# Download bfg.jar from https://rtyley.github.io/bfg-repo-cleaner/
java -jar bfg.jar --replace-text passwords.txt your-repo.git

After rewriting history, force-push to the remote:

git push --force --all
git push --force --tags

Important: force-pushing rewrites history for all collaborators. Notify your team before doing this. Anyone with a local clone will need to re-clone or rebase their work.

Remember: rewriting history does not guarantee the key was not already copied. Always rotate the key first, then clean up the history. The history cleanup prevents future exposure, but it cannot undo exposure that already happened.

CI Pipeline Scanning

Pre-commit hooks run on developer machines, but developers can bypass them (intentionally or accidentally). A CI pipeline scan provides a second layer of detection that cannot be bypassed.

Add gitleaks to your GitHub Actions workflow:

# .github/workflows/security.yml
name: Secret Scanning
on: [push, pull_request]

jobs:
  gitleaks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

This scans the full commit history on every push and pull request. If a secret is found, the workflow fails and blocks the merge.

For GitLab CI:

# .gitlab-ci.yml
secret_scanning:
  image: zricethezav/gitleaks:latest
  script:
    - gitleaks detect --source . --verbose

Combine pre-commit hooks (fast, local) with CI scanning (reliable, cannot be bypassed) for comprehensive coverage.

Remediation Checklist When a Key Is Found

If you discover a Paystack key in your git history, follow this checklist in order:

  1. Rotate the key immediately. Log in to the Paystack dashboard, go to Settings, API Keys & Webhooks, and generate new keys. The old key becomes invalid instantly.
  2. Update all deployments. Replace the old key with the new one in all environment variable configurations (Vercel, Railway, Render, or wherever your app runs).
  3. Verify payments still work. Process a test transaction to confirm the new key is active.
  4. Check for unauthorized activity. Review your Paystack transaction history and transfer logs for any activity you did not initiate during the exposure window.
  5. Clean the git history. Use git filter-repo or BFG to remove the key from all commits.
  6. Force-push the cleaned history. Notify your team first.
  7. Install pre-commit hooks. Prevent the same mistake from happening again.
  8. Document the incident. Record when the key was exposed, when it was rotated, and what changes you made to prevent recurrence.

The order matters. Rotating the key is the first priority because every second the old key is active, an attacker with access to your repository can use it. History cleanup can wait until after the key is rotated and deployments are updated.

For the full incident response process, see what to do if your Paystack secret key leaked.

The Right .gitignore for Payment Projects

Your .gitignore should block all files that might contain secrets. Here is a comprehensive list for a Paystack integration project:

# Environment files
.env
.env.local
.env.development
.env.staging
.env.production
.env.*.local

# IDE and editor files (may contain project-level env configs)
.idea/
.vscode/settings.json

# OS files
.DS_Store
Thumbs.db

# Node modules (dependencies may cache env values)
node_modules/

# Build output (may embed env values)
dist/
.next/
build/

The .env.example file should not be in .gitignore. It contains placeholder values and serves as documentation for your team.

Verify your .gitignore is working by running git status after creating a .env file. If the .env file shows up as untracked, your .gitignore is misconfigured or the file was tracked before the .gitignore entry was added. In that case, run git rm --cached .env to untrack it.

Key Takeaways

  • A Paystack secret key committed to any git repository, even a private one, should be treated as compromised. Rotate the key immediately.
  • Pre-commit hooks using tools like git-secrets or gitleaks catch keys before they enter the repository. This is the most effective prevention.
  • GitHub secret scanning can detect Paystack key patterns in public repositories and alert you automatically.
  • Removing a committed key from git history requires git filter-repo or BFG Repo-Cleaner. Simply deleting the file and committing again does not remove the key from history.
  • A .gitignore entry for .env files must exist before the first commit. Adding it later does not remove already-tracked files.
  • Automated scanning in CI pipelines provides a second layer of detection for keys that bypass pre-commit hooks.

Frequently Asked Questions

I committed a test key (sk_test_), not a live key. Do I still need to rotate it?
Yes, rotate it. A test key gives full API access in test mode, including reading all test customer data and transaction history. While it cannot move real money, it still exposes your account structure and should be treated as a secret.
Can I use a .env file in production?
Most production environments do not use .env files. Instead, set environment variables through your hosting platform dashboard (Vercel, Railway, Render) or a secrets manager. The .env file is primarily for local development.
How do pre-commit hooks interact with CI scanning?
They complement each other. Pre-commit hooks catch secrets before they enter the local repository. CI scanning catches secrets that bypass pre-commit hooks (because hooks can be skipped with --no-verify). Use both for comprehensive protection.
Does deleting a branch that contained the key remove it from history?
Not reliably. The commit may still be referenced by reflog entries, merge commits, or other branches. The only way to reliably remove a key from history is to rewrite the history using git filter-repo or BFG.
What if my key was committed months ago and I just discovered it?
Rotate the key immediately regardless of how long ago it was committed. Then check your Paystack dashboard for unauthorized transactions or transfers during the exposure window. Finally, clean the git history and install prevention hooks.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon