Why Developers Hardcode Secrets
Nobody hardcodes credentials because they think it's a good idea. They do it because they're under pressure, because it works, and because the secure alternative requires setup they don't have time for right now.
- Speed: Hardcoding works immediately. Setting up proper secrets management takes hours the developer doesn't have before the demo.
- Testing: "I'll only use this in the dev branch." Then dev gets merged, or the test credential has production access.
- Habit: Following a tutorial that shows
API_KEY = "sk-abc123"in the example code. The tutorial works. The pattern sticks. - Lack of alternatives: Teams without a secrets management system have no obvious alternative to environment variables or hardcoding.
Scale of the problem: GitHub's secret scanning program detected over 1 billion exposed secrets on the platform in 2023. The majority were valid, active credentials at the time of detection.
Real-World Credential Breaches
The pattern repeats with depressing regularity:
- A startup's AWS credentials hardcoded in a public GitHub repository. Attacker finds them within hours using automated scanners. $50,000 in compute charges before anyone notices.
- A developer accidentally pushes a config file containing a Stripe live API key. The key has full charge access. Fraudulent charges begin within 20 minutes.
- An intern's test project, pushed to a company GitHub org, contains database credentials. The "test" database turns out to contain a copy of production customer data.
Automated credential scanners are running constantly: There are bots indexing GitHub commits in real-time, specifically looking for exposed credentials. The average time between a credential being pushed and it being found and used is measured in minutes, not days.
Git History Never Forgets
The most dangerous misconception about credential exposure: that deleting the file and pushing a new commit fixes the problem. It doesn't. Git is an append-only data structure. The original commit containing the secret still exists in full in the repository history.
# Secret exposed in commit 3a7f2c1, then "deleted" in commit b8e4d5a # Both commits are visible to anyone with repo access: git log --oneline # b8e4d5a Remove hardcoded API key # 3a7f2c1 Add payment integration <-- secret is HERE, permanently # Attacker can always retrieve it: git show 3a7f2c1:config.py | grep API_KEY # Even if the repo is made private, the secret was already public. # Assume it has been found. Rotate it immediately.
The only correct response when a secret has been committed to git β even if removed immediately β is to rotate it. Treat it as compromised. Then use git-filter-repo or BFG Repo Cleaner to purge the history if needed, but the rotation is more urgent.
Where Secrets Hide (It's Not Just Source Code)
Developers focus on .py, .js, and .env files. But secrets appear in many unexpected places:
- Configuration files:
application.yml,config.json,settings.ini,appsettings.json - Infrastructure code: Terraform
.tfvarsfiles, Kubernetes manifests, Helm chart values - CI/CD configuration:
.github/workflows/,.gitlab-ci.yml, Jenkins files with credentials - Test fixtures: Test data files that use "real-looking" but accidentally real credentials
- Log files: Applications that log request headers or API calls often capture Bearer tokens and API keys
- Docker images: Build arguments, environment variables baked into layers, cached layers with downloaded credentials
- Notebooks: Jupyter notebooks that include API calls with live credentials, then get pushed to repos
Environment Variables β Better but Not Enough
Environment variables are significantly better than hardcoded values β they don't appear in source code, and different values can be used per environment. But they have their own risks:
- Process listing: On Linux, process environment variables are accessible via
/proc/[pid]/environto processes with the same UID. A compromised process can read sibling processes' env vars. - .env files committed to git: Teams use
.envfiles locally, which are fine as long as they're in.gitignore. They frequently aren't. - CI/CD variable exposure: CI environment variables can appear in logs if the application logs its startup configuration. Many do.
- Container environment visibility:
docker inspectexposes all environment variables to anyone with Docker socket access.
Proper Secrets Management Solutions
The right answer is a dedicated secrets management system that provides access control, audit logging, rotation, and short-lived credentials:
- HashiCorp Vault: The most feature-complete open-source option. Dynamic secrets, fine-grained access policies, automatic rotation.
- AWS Secrets Manager / Parameter Store: Good if you're already AWS-native. Automatic rotation for RDS credentials, tight IAM integration.
- Azure Key Vault / GCP Secret Manager: Cloud-provider equivalents with similar capabilities.
- Infisical / Doppler: Developer-friendly SaaS options with good DX for teams without dedicated infrastructure expertise.
Short-lived credentials are the goal: The best secrets management systems can provide credentials that expire in minutes or hours. Even if a credential leaks, it's useless by the time an attacker tries to use it.
How Secrets Scanners Work
Secrets scanners use a combination of techniques to detect credentials in code:
- Regex patterns: AWS access keys follow the pattern
AKIA[0-9A-Z]{16}. GitHub PATs matchghp_[a-zA-Z0-9]{36}. These patterns are well-known and reliably detected. - Entropy analysis: Secrets tend to be high-entropy strings β detecting strings with Shannon entropy above a threshold flags potential credentials even without a known pattern.
- Context analysis: Lines containing words like
password,secret,token,key,api_keyassigned a string value are high-priority candidates. - Allowlists: Known false positives (example values, test placeholders) are suppressed.
Pre-Commit Hooks: The Last Line Before Push
repos: - repo: https://github.com/gitleaks/gitleaks rev: v8.18.0 hooks: - id: gitleaks - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: detect-private-key - id: detect-aws-credentials
Pre-commit hooks run on the developer's machine before each commit, catching secrets before they ever reach the remote. They're not foolproof β developers can bypass them β but they catch accidental exposures, which are the majority of cases.
Building a Secrets Hygiene Programme
- Scan immediately: Run a secrets scanner on your entire git history today. Know what's already exposed.
- Rotate all found secrets: Treat everything found as compromised. Rotate now, investigate later.
- Install pre-commit hooks: Deploy gitleaks or similar across all developer machines and CI pipelines.
- Set up CI scanning: Scan every PR for new secrets β not just the files changed, but the full commit history added.
- Adopt secrets management: Pick a solution and migrate the most critical secrets first (cloud credentials, database passwords, payment keys).
- Audit access: Secrets management systems provide audit logs β review who has accessed which secrets and when.
- Regular rotation schedule: Even well-managed secrets should rotate regularly. Automate rotation for cloud credentials where possible.
Find Secrets in Your Codebase
AquilaX secrets scanning covers 400+ secret types across your full git history, Docker images, and configuration files β finding what you don't know is there.
Start Free Scan