Secrets Management in DevSecOps: Vault, Rotation & Zero Hardcoded Credentials
The Hardcoded Secrets Epidemic
Secrets — API keys, database passwords, tokens, private keys, certificates — are the keys to your kingdom. And they're everywhere they shouldn't be.
GitGuardian 2025 State of Secrets Sprawl: Analyzed 1.5 billion git commits and found 12.8 million new secret occurrences in public repositories in 2024 alone — a 28% increase over 2023. One in every 400 commits contains at least one hardcoded secret.
| Metric | Value | Source |
|---|---|---|
| New secrets found in public repos (2024) | 12.8 million | GitGuardian 2025 |
| Commits containing a secret | 1 in 400 | GitGuardian 2025 |
| Avg time a leaked secret stays valid | 5+ years | GitGuardian |
| Orgs with secrets in their codebase | 73% | CyberArk 2025 |
| Breaches involving stolen credentials | 49% | Verizon DBIR 2025 |
| Most common leaked secret type | Cloud provider keys (AWS, GCP, Azure) | GitGuardian |
Types of Secrets and Where They Leak
| Secret Type | Common Leak Locations | Impact if Exposed |
|---|---|---|
| AWS Access Keys | Git repos, .env files, CI/CD logs | Full AWS account access |
| Database passwords | Docker configs, config files | Complete data breach |
| API keys (Stripe, SendGrid) | Client-side JS, mobile apps | Financial fraud, spam |
| JWT signing keys | Source code, config files | Token forgery, auth bypass |
| SSH private keys | Git repos, shared folders | Server access |
| TLS private keys | Docker images, config repos | Traffic interception |
| OAuth client secrets | Frontend code, public repos | Account takeover |
| Encryption keys | Environment variables, code | Data decryption |
Secrets Detection
Pre-Commit Detection (Prevent Leaks)
# Install gitleaks as pre-commit hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
# Alternatively, install gitleaks globally
brew install gitleaks
# Scan entire repository history
gitleaks detect --source . --verbose
# Scan only staged changes (fast, for pre-commit)
gitleaks protect --staged --verbose
CI/CD Detection (Catch What Slipped Through)
# GitHub Actions — Block PRs with secrets
name: Secrets Detection
on: [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 }}
Secrets Detection Tools Comparison
| Tool | Type | Speed | Custom Rules | Best For |
|---|---|---|---|---|
| Gitleaks | Pre-commit + CI | Fast | Yes (TOML) | General-purpose |
| TruffleHog | Git history + live | Medium | Yes | Deep historical scanning |
| GitGuardian | SaaS + CI | Fast | Yes | Enterprise, remediation tracking |
| detect-secrets (Yelp) | Pre-commit | Fast | Yes (plugins) | Minimal false positives |
| Semgrep | SAST + secrets | Medium | Yes (YAML) | Combined code + secrets scanning |
Secrets Management Solutions
HashiCorp Vault
// Node.js — Fetching secrets from HashiCorp Vault
import Vault from "node-vault";
const vault = Vault({
apiVersion: "v1",
endpoint: process.env.VAULT_ADDR,
token: process.env.VAULT_TOKEN, // Use AppRole in production
});
async function getDatabaseCredentials() {
// Dynamic secret — Vault generates temporary credentials
const result = await vault.read("database/creds/myapp-readonly");
return {
username: result.data.username, // Temporary username
password: result.data.password, // Temporary password
ttl: result.lease_duration, // Expires in seconds
leaseId: result.lease_id, // For renewal/revocation
};
}
AWS Secrets Manager
import {
SecretsManagerClient,
GetSecretValueCommand,
} from "@aws-sdk/client-secrets-manager";
const client = new SecretsManagerClient({ region: "us-east-1" });
async function getSecret(secretName: string): Promise<Record<string, string>> {
const command = new GetSecretValueCommand({ SecretId: secretName });
const response = await client.send(command);
if (response.SecretString) {
return JSON.parse(response.SecretString);
}
throw new Error("Secret not found");
}
// Usage
const dbConfig = await getSecret("prod/database/credentials");
// dbConfig = { host: "...", username: "...", password: "..." }
Solution Comparison
| Feature | HashiCorp Vault | AWS Secrets Manager | Azure Key Vault | GCP Secret Manager |
|---|---|---|---|---|
| Dynamic secrets | Yes (databases, cloud, PKI) | No | No | No |
| Auto-rotation | Yes | Yes (Lambda) | Yes | Yes (Cloud Functions) |
| Transit encryption | Yes (built-in) | Via KMS | Via HSM | Via Cloud KMS |
| Multi-cloud | Yes | AWS only | Azure only | GCP only |
| Open source | Yes (OSS version) | No | No | No |
| Price | Free (OSS) / Enterprise | $0.40/secret/month | $0.03/10K operations | $0.06/10K operations |
Automated Secret Rotation
Why Rotation Matters
- Limits blast radius — If a secret is leaked, it's only valid until the next rotation
- Compliance requirement — PCI-DSS, SOC 2, HIPAA all require credential rotation
- Reduces insider risk — Former employees' knowledge of secrets becomes useless
Rotation Schedules
| Secret Type | Rotation Frequency | Method |
|---|---|---|
| Database passwords | 24 hours (dynamic) or 30 days | Vault dynamic secrets or AWS rotation Lambda |
| API keys | 90 days | Automated key regeneration |
| TLS certificates | 30-90 days | cert-manager / Let's Encrypt auto-renewal |
| SSH keys | 90 days or per-session | Vault SSH Secrets Engine |
| JWT signing keys | 7 days (with key versioning) | Custom rotation with backward compatibility |
| Cloud access keys | Never (use IAM roles instead) | Eliminate static cloud keys entirely |
CI/CD Secrets Security
| Rule | Implementation |
|---|---|
| Never echo secrets in logs | set +x in bash; mask in CI config |
| Use CI-native secret storage | GitHub Actions secrets, GitLab CI/CD variables |
| Rotate CI/CD secrets quarterly | Automated key rotation workflow |
| Limit secret scope | Repo-level, not org-level secrets |
| Audit secret access | Review who/what accessed which secrets |
| No secrets in Docker build args | Use multi-stage builds + runtime injection |
| Least-privilege tokens | Scoped to specific repos/environments |
Zero Hardcoded Credentials Maturity Model
| Level | Description | Controls |
|---|---|---|
| Level 0: Wild West | Secrets in source code, shared plaintext files | No controls |
| Level 1: Awareness | Secrets moved to environment variables | .env files, CI secrets |
| Level 2: Managed | Centralized secrets management | Vault/AWS SM, rotation |
| Level 3: Dynamic | Short-lived, auto-rotating credentials | Dynamic secrets, certificate management |
| Level 4: Zero Standing | No persistent credentials exist | Just-in-time access, identity-based auth |
Further Reading
- DevSecOps Complete Guide — Comprehensive DevSecOps implementation
- Shift-Left Security — Security in the SDLC
- GitGuardian (2025), "State of Secrets Sprawl" — Secrets leak statistics
- HashiCorp Vault Documentation — Vault reference
- Verizon (2025), "Data Breach Investigations Report (DBIR)" — Credential breach statistics
Advertisement
Free Security Tools
Try our tools now
Expert Services
Get professional help
OWASP Top 10
Learn the top risks
Related Articles
Software Supply Chain Security: Defending Against Modern Threats
How to protect your applications from supply chain attacks targeting dependencies, build pipelines, and deployment processes.
Container Security Best Practices for Production
Secure your containerized applications from image building to runtime with these battle-tested practices.
DevSecOps: The Complete Guide 2025-2026
Master DevSecOps with comprehensive practices, automation strategies, real-world examples, and the latest trends shaping secure development in 2025.