December 2024. A developer at a fintech startup committed a .env file to a public GitHub repo. It contained a Stripe live key, a Postgres connection string, and an AWS secret access key. A bot found it in 11 seconds. The Stripe key processed $14,000 in fraudulent charges. The AWS key spun up crypto miners across three regions.
The developer had a .gitignore entry. Added months ago. But they ran git add . from a new machine where the gitignore wasn't in place. One command. Fourteen thousand dollars.
This isn't rare. GitGuardian's 2024 State of Secrets Sprawl report found 12.8 million secrets exposed in public GitHub repositories that year. The part that should keep you up at night: 70% were still valid 5 days after detection. Developers aren't just leaking secrets — they're not rotating them after the leak.
The fix isn't better security tools. It's better habits.
7 habits that leak your credentials
1. Storing secrets in .env files
We ran find ~/projects -name ".env" | wc -l on a team member's machine last year. The answer was 47. Forty-seven plaintext files, zero authentication, sitting in project directories. Some contained the same Cloudflare API token. Some had credentials for services nobody remembered signing up for.
The .env file was a Ruby convention from 2012. Designed for convenience, not security. No encryption, no access control, no audit trail. Every process running as your user can read every .env file on your machine — including AI coding assistants that treat your .env as just another project file.
Fix: Move secrets to your OS credential store. On macOS, that's the Keychain — encrypted, backed by the Secure Enclave, protected by Touch ID. Load secrets at runtime with eval, not from files on disk.
2. Passing secrets as CLI arguments
A colleague shared their screen to debug a failing API call and typed curl -H "Authorization: Bearer sk_live_4eC39HqLyjWDarjtT1zdp7dc" https://api.stripe.com/v1/charges. That token was now in their shell history (~/.zsh_history), visible in ps aux output, stored in the terminal's scrollback, and captured in the screen recording they were making for a bug report.
Four copies of a production Stripe key from one command.
Fix: Read credentials from environment variables or stdin. Use --clipboard for storing secrets. Never type or paste a secret value as a command-line argument. If you need to pass auth to curl, use -H @- and pipe it in.
3. Pasting secrets into AI chats
"Here's my .env file, can you help me debug this deployment?" We've seen this exact message in public Discord servers. But even in private conversations with ChatGPT or Claude, that secret is now on someone else's servers. In conversation history. Possibly in training data. Backed up, replicated, and stored in ways you can't control or audit.
Your message hits an API endpoint, gets logged for abuse detection, stored in a conversation database, potentially queued for human review, and retained per the provider's data retention policy. You just handed your production database URL to a system with more copies of it than you can count.
That's the best case — talking to a legitimate provider. Wrapper apps and browser extensions that proxy AI conversations add their own logging layers.
Fix: Never paste credentials into any chat. Use a DLP guard that scans outbound text for known secret patterns. If an AI agent needs a secret, it should flow through the OS — not through the conversation.
4. Sharing secrets via Slack and email
We searched a team Slack workspace for "API_KEY" last month. 23 results. Database passwords in DMs, Stripe keys in channel messages, SSH credentials in thread replies. All searchable by anyone in the workspace. All stored on Slack's servers indefinitely.
Rotating these credentials doesn't clean up the Slack messages. The old values sit there forever — a historical record of every credential your team has ever used.
Fix: Give people access to the credential store, not the credentials. If someone needs the staging database URL, add them to the org's prefix in your secrets manager. They pull it themselves with their own Touch ID. No secret value ever transits a messaging platform.
5. Duplicating secrets across projects
One Cloudflare API token in 6 different .env files. When we rotated it, we updated the two active projects and forgot the other four. Three months later, a deploy failed on a dormant project still using the old token. That's the best outcome. The worst is not noticing — assuming the old token is revoked when it's still live in four locations.
Fix: One secret, one location. Reference by path: shared/CLOUDFLARE_API_TOKEN. Every project pulls from the same source. Rotate once, it's rotated everywhere. No copies, no sync, no drift.
6. Never rotating credentials
We audited our API keys last quarter. One Stripe key had been active since 2023. Same key, full access, never rotated. It had been in shell history, in at least two deleted .env files (still in filesystem backups), and in a Slack DM sent to a contractor.
The median age of a leaked secret on GitHub is over 2 years, according to GitGuardian. These aren't abandoned test keys. They're production credentials that nobody rotated because nothing visibly broke.
Fix: Set expiry dates on secrets. Get warnings when they approach expiry. Use scoped, short-lived tokens where services support them. Run noxkey ls periodically to see what you have — if you don't recognize a key, rotate or revoke it.
7. No separation between human and agent access
Your AI coding assistant uses the same API token you do. Same permissions. Same access scope. When Claude Code runs a curl command to debug your API, it makes that request with your full production credentials. If it hallucinates a destructive API call, it works — because it has your identity.
This isn't hypothetical. We've watched agents construct valid API calls using credentials inherited from the shell environment. The agent wasn't malicious. It was doing exactly what we asked. It just used live credentials in the request.
Fix: Detect when an agent is requesting secrets. Give agents encrypted, scoped access. Block bulk export commands. The agent gets what it needs to function, but never raw credential values in its text context.
5-minute credential security audit
Run these commands right now. They tell you exactly where you stand.
# How many .env files do you have?
find ~/projects -name ".env" -o -name ".env.local" -o -name ".env.production" 2>/dev/null | wc -l
# Any live Stripe keys on disk?
grep -r "sk_live_" ~/projects --include="*.env" --include="*.env.*" -l 2>/dev/null
# Any AWS keys on disk?
grep -r "AKIA" ~/projects --include="*.env" --include="*.env.*" -l 2>/dev/null
# Secrets in shell history?
grep -E "(sk_live|sk_test|AKIA|ghp_|glpat-)" ~/.zsh_history ~/.bash_history 2>/dev/null | wc -l
# Old keys in git history? (run inside a repo)
git log --all -p | grep -E -c "sk_live|AKIA|ghp_" 2>/dev/null
We ran grep -r 'sk_live' ~/projects and found 14 matches. Fourteen places a production Stripe key sat on disk. Some in .env files, some in test fixtures, one in a README with an unsanitized example. That number should be zero.
The minimum viable credential management stack
You don't need an enterprise security platform. For an individual developer or small team:
OS Credential Store
macOS Keychain or Linux Secret Service. Not files.
Biometric Auth
Touch ID on every read. Not a master password you type once.
CLI Workflow
Must work in terminals, scripts, and CI.
Agent Detection
Different behavior when an AI tool requests secrets.
Expiry Tracking
Know when your tokens are about to expire.
Single Source of Truth
One secret, one location. No copies, no sync, no drift.
The credential hygiene checklist
Print this. Tape it to your monitor. Fix one per week.
- Run the 5-minute audit above. Count your .env files.
- Install a Keychain-backed secrets manager — download NoxKey.
- Import your most-used project's .env:
noxkey import org/project .env - Delete the .env file. Actually delete it.
- Search Slack/email for credentials you've shared. Rotate them.
- Check shell history for secrets:
grep -E "(sk_live|sk_test|AKIA|ghp_|glpat-)" ~/.zsh_history ~/.bash_history - Set up a pre-commit hook that blocks secrets (
git-secretsorgitleaks). - Consolidate duplicated keys to a single
shared/path. - Review token ages. Rotate anything older than 90 days.
- Set up a DLP guard for AI agent output.
Start with one
You won't fix all seven habits today. You don't need to. The highest-impact change is moving from .env files to your OS credential store. On macOS:
# Install NoxKey — download from https://noxkey.ai
# Import your existing .env
noxkey import myorg/project .env
# Verify
noxkey ls myorg/project/
# Delete the .env
rm .env
# Use secrets in your shell
eval "$(noxkey get myorg/project/API_KEY)"
Three minutes. All your secrets are in the Keychain, encrypted, behind Touch ID. The .env file is gone.
Credential leaks aren't sophisticated attacks — they're habits. Seven common habits account for nearly all developer secret exposure. You don't need an enterprise platform to fix them. Move secrets from files to your OS credential store, add biometric auth, detect agent access, and fix one habit per week. Start by deleting your .env files today.
Pick one habit. Fix it today. Come back for the next one.