Secrets Management

By Davy Rogers

We've all pushed secrets to Git. Here's how to stop.

What counts

Database passwords, API keys, encryption keys, OAuth client secrets, SSH keys, JWT signing secrets. If an attacker could use it to access something - it's a secret.

Where secrets should never be

Source code: Committed to Git = compromised forever. History preserves everything.

Config files in version control: .env checked in. Private repo still gets cloned to laptops, CI runners.

Client-side code: Anything in JS bundle is public.

Log files: Connection strings in startup logs, tokens in request logs.

Error messages: Stack traces with secrets.

Where secrets should be

Environment variables: Simple approach. Limitations: no access control, auditing, rotation.

Secrets managers: AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault.

What you get: access control, audit logs, automatic rotation, versioning, encryption.

# AWS Secrets Manager
client = boto3.client("secretsmanager")
response = client.get_secret_value(SecretId="myapp/database")
secret = json.loads(response["SecretString"])

Rotation

Rotate regularly. Rotate immediately after suspected compromise.

Design for rotation:

  • Don't cache secrets forever. Re-read periodically.
  • Support dual credentials during rotation.
  • Test rotation in staging first.

Detection

Pre-commit hooks: gitleaks, detect-secrets, truffleHog.

gitleaks detect --source . --verbose

CI/CD scanning: Second line of defence.

GitHub/GitLab scanning: Built-in alerts for known secret patterns.

When a secret leaks

  1. Revoke immediately
  2. Rotate to new value
  3. Audit usage
  4. Fix root cause

The takeaway

Never put secrets in code, logs, error messages, or client-side bundles. Environment variables for simple. Secrets managers for production. Rotate regularly. Use pre-commit hooks.

When a secret leaks: revoke first, investigate second.

Want a professional to look at it?Get an AppSec Health Check.