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.
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.
Before setting up prevention, check whether your repository already contains committed keys. Here are three ways to scan your git history:
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.
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.
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.
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
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.
# 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.
# 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.
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 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:
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.
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.
# 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
# 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.
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.
If you discover a Paystack key in your git history, follow this checklist in order:
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.
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.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course