Why this matters
An API key, a token or a database password sitting in your code isn't just "a line" — it's an open door to your money, your data and your infrastructure. Bots scan GitHub, npm, Docker Hub and pastebins round the clock with regexes like sk-..., AKIA..., ghp_... — a public key is found within seconds or minutes, long before you get around to deleting it.
Real-world scenario. A developer commits config.js with an OpenAI key and AWS credentials, then pushes a fix removing it 4 minutes later. Too late: a bot had already pulled the key out of the commit history. By morning there's a bill for hundreds of dollars of someone else's model traffic and mining instances spun up in his AWS account. Git history remembers everything: even git rm plus a new commit doesn't remove the secret from earlier revisions.
What to do (step by step)
- Move every secret into
.envand add the file to.gitignoreright away — before the first commit:
# .gitignore
.env
.env.*
!.env.example # a template with no values — fine to commit
# .env (NOT committed)
OPENAI_API_KEY=sk-...
DATABASE_URL=postgres://user:pass@host:5432/db
- A separate scoped token for each project, with the minimum rights it needs (read-only wherever writes aren't required).
- Rotate on a schedule — and immediately at the first hint of trouble. If a key has ever landed in git, a chat or a screenshot, treat it as compromised and reissue it.
- For a team, use a secrets manager (1Password / HashiCorp Vault / Doppler), not DMs and not a pinned chat message.
- Add a pre-commit safety net so a secret physically can't make it into a commit:
pip install pre-commit detect-secrets
detect-secrets scan > .secrets.baseline # create the baseline
# then add the detect-secrets hook to .pre-commit-config.yaml
- If the key is already in your history, deleting the file isn't enough. Rotate the key, then scrub the history with
git filter-repo(or BFG) and force-push once the team agrees.
What NOT to do
- Don't commit keys to git — not even "temporarily", not even to a private repo (private repos can go public).
- Don't paste tokens into chats, AI bots, issues, screenshots or public repositories.
- Don't hand one "all-powerful" key to every service at once — compromising one compromises everything.
- Don't hardcode secrets "just for a minute, to test" — those are exactly the commits that leak.
- Don't bake
.envinto a Docker image withCOPY . .— it stays in the image layers.
Self-check
- There isn't a single real key in the repo:
git grep -nE "(sk-|AKIA|ghp_|xox|-----BEGIN)"finds nothing live. -
.envis in.gitignore, andgit ls-files | grep -i envdoesn't show a production.env. - Every token has a limited scope, and it's clear which project uses it.
- I know how and where to reissue each key (there's a link or a procedure).
- I've run a secret scanner over the repo at least once.
Tools
- Team secrets managers: 1Password, HashiCorp Vault, Doppler, Infisical.
- Secret scanners: gitleaks, detect-secrets, trufflehog.
- Scrubbing git history: git-filter-repo, BFG Repo-Cleaner.
- Leak detection: GitHub Secret Scanning (Push Protection) — turn it on in the repository settings.