📋 What Is It?
Insufficient Credential Hygiene is the failure to properly handle the large volume of secrets that flow through a modern pipeline. Every build, test, deploy, and release step needs credentials - cloud keys, registry tokens, database passwords, signing keys, SaaS tokens - and each is created, stored, passed between steps, printed, cached, and (ideally) rotated. A secret is not stored in one place; it is a river that flows through code, Git history, logs, environment variables, artifacts, and cloud IAM. When any link is careless, a secret leaks, lingers, or is over-privileged, and whoever obtains it inherits the access it grants.
⚠️ Common Exploits
- Hardcoded Secrets: Keys pasted into pipeline YAML, code, or Dockerfiles
- Secrets in Git History: Removed from HEAD but still recoverable from old commits
- Long-Lived Static Keys: Broadly scoped cloud keys, years old, never rotated
- Secrets in Build Logs:
set -xtracing andechodefeating masking - Over-Shared "God" Tokens: One token reused by every pipeline and repo
- Secrets Baked into Artifacts: Build-time ARG/ENV persisting in image layers
- No Scanning: Leaks discovered by the attacker's scanner first
🔴 Attack Flow
↓
2. Validate scope (whoami / list / describe)
↓
3. Reuse against cloud / registry / DB / prod
↓
4. Pivot via over-shared tokens; mint new keys
↓
5. IMPACT: Full account or supply-chain takeover - no exploit needed!
❌ Vulnerable Code
✅ Secure Code
✓ Prevention Checklist
- Keep secrets out of code, YAML, and Dockerfiles entirely
- Prefer short-lived OIDC-federated credentials over static keys
- Scope secrets per environment / pipeline / step, least privilege
- Rotate on a schedule AND immediately on suspected exposure
- Rotate first, then purge from history (deletion is not the fix)
- Never
echosecrets; keepset -xoff around them - Register dynamically derived secrets with the log masker
- Use BuildKit secret mounts; never bake secrets into images
- Scan pre-commit and in CI across full history
- No standing human access to production secrets
- Use ephemeral, single-use runners
- Isolate untrusted workloads from production secrets
🔍 Detection & Tools
Secret Scanning:
Secrets & Identity:
How to Test:
- Scan full Git history:
trufflehog git file://. --only-verified - Grep pipeline logs for token patterns (
gh[pousr]_...) - Inspect image layers:
docker history --no-trunc - List cloud key age and scope for CI IAM users
- Confirm a "removed" secret is unrecoverable from old commits
- Verify a leaked test secret was rotated, not just deleted
🌍 Real-World Incident Classes
- Cloud keys committed to public repos: Automated bots continuously scan public code-hosting for credential patterns and reuse hits within minutes - often before the developer finishes pushing follow-up commits.
- Secrets recoverable from Git history: A team "removes" a committed secret but never rotates it; anyone who clones - including forks, mirrors, and former contributors - recovers the still-valid value.
- Secrets leaked through build logs: Debug tracing or an
echopushes a secret past CI masking into retained, widely readable, forwarded logs. - Secrets baked into container images: Build-time ARGs persist in layers and travel to every registry, laptop, and environment the image reaches.
Note: exact leak counts vary by report and year - treat any single figure as illustrative. The durable takeaway is that secrets leak constantly, are found fast, and are reused faster.
📌 Quick Tips
- DO NOT hardcode secrets in code or YAML
- DO NOT treat deletion as remediation - rotate
- DO NOT share one token across all pipelines
- DO mint short-lived OIDC credentials
- DO scope secrets per environment and step
- DO scan repos, history, and logs continuously
- DO use ephemeral runners and Vault JIT access
📜 Compliance
Related Standards:
- SLSA Provenance & isolated build environments
- NIST 800-53 IA-5 - Authenticator Management
- CIS Software Supply Chain Benchmarks
- PCI-DSS Req 8 / 3.5 - Key Management
- SOC 2 CC6.1