Secrets Management: Keeping Credentials Out of Your Code
Hardcoded passwords are a security vulnerability and a compliance failure. AWS Secrets Manager, environment variables, and secret rotation done right. Skill 15 of 20.
business skills
security
secrets management
AWS
DevOps
Author
Jong-Hoon Kim
Published
April 24, 2026
1 Why secrets management is non-negotiable
A developer pushes a database connection string to a public GitHub repository by accident. Within 15 minutes, a bot discovers it and begins exfiltrating patient data. This is not a hypothetical — it happens dozens of times per day across GitHub (1).
Any health department client will conduct at minimum a basic security review before signing a contract. “Where are your database passwords stored?” is question one. “In the code” or “in a .env file in the repository” ends the conversation.
Proper secrets management is both a security practice and a commercial requirement. It is also straightforward to implement.
2 The hierarchy of bad to good
❌ Hardcoded in source code: password = "secret123"
❌ In .env committed to git: DATABASE_URL=postgresql://...
⚠ In .env not committed: ok for local dev only
✓ Environment variable at runtime: -e DB_PASSWORD=...
✓ AWS Secrets Manager: retrieved at startup, rotated automatically
✓ HashiCorp Vault: enterprise-grade, audited, dynamic secrets
3 Environment variables: the baseline
The minimum safe approach: never put secrets in source code. Pass them as environment variables at runtime.
db_url <-Sys.getenv("DATABASE_URL", unset =stop("DATABASE_URL not set"))api_key <-Sys.getenv("API_SECRET_KEY", unset =stop("API_SECRET_KEY not set"))
# Demonstrate safe secret reading pattern in Rsafe_get_env <-function(key, required =TRUE) { val <-Sys.getenv(key, unset =NA_character_)if (is.na(val) ||nchar(val) ==0) {if (required) stop(sprintf("Required environment variable '%s' not set.", key))return(NULL) } val}# Show what the pattern producestryCatch(safe_get_env("DATABASE_URL"), # this env var is not seterror =function(e) cat("Error (expected):", conditionMessage(e), "\n"))
Error (expected): Required environment variable 'DATABASE_URL' not set.
# Simulate a set variableSys.setenv(DT_API_KEY_TEST ="fake-key-for-demo-only")cat("Key found:", nchar(safe_get_env("DT_API_KEY_TEST")), "chars\n")
Key found: 22 chars
Sys.unsetenv("DT_API_KEY_TEST")
4 AWS Secrets Manager
For production, AWS Secrets Manager (2) stores secrets encrypted at rest, controls access via IAM, and supports automatic rotation.
# Store a secret (one-time)aws secretsmanager create-secret \--name"dt/prod/db-password"\--description"TimescaleDB password for prod environment"\--secret-string'{"username":"dtuser","password":"S3cur3P@ss"}'# Retrieve at runtime (in startup script)SECRET=$(aws secretsmanager get-secret-value \--secret-id"dt/prod/db-password"\--query SecretString --output text)DB_USER=$(echo$SECRET|python3-c"import sys,json; print(json.load(sys.stdin)['username'])")DB_PASS=$(echo$SECRET|python3-c"import sys,json; print(json.load(sys.stdin)['password'])")exportDATABASE_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:5432/dtdb"
When rotation fires, Secrets Manager calls a Lambda function that updates the RDS user’s password and stores the new value. Your application always calls get_secret() at startup — it never needs to know a rotation happened.
6 Scanning for leaked secrets
Add git-secrets or gitleaks as a pre-commit hook to prevent accidental commits:
# Install gitleaksbrew install gitleaks # or download binary# Scan the whole repo historygitleaks detect --source . --log-opts HEAD# Add as pre-commit hookgitleaks protect --staged
7 The secrets audit checklist
Secrets management audit checklist
Item
Priority
No secrets in source code (grep for common patterns)
Critical
.env files in .gitignore
Critical
Secrets passed as environment variables or Secrets Manager
Critical
Different credentials per environment (dev ≠ prod)
High
Database users have minimum required permissions
High
API keys have expiry dates and can be revoked
High
Rotation policy configured for long-lived secrets
Medium
Audit trail: who accessed which secret and when
Medium
Every item marked “Critical” will be raised in a security review by a health department procurement team. Tick them off before your first enterprise client conversation.