JustAppSec

Secrets Management

Vaults, environment variables, rotation — keeping credentials out of code.

0:00

Secrets — API keys, database passwords, encryption keys, tokens — are the most valuable targets in your application. A single leaked secret can compromise your entire system. This lesson covers how to store, access, rotate, and revoke secrets properly.

What counts as a secret

Anything that grants access to a system or data:

  • Database connection strings and passwords
  • API keys and tokens (Stripe, AWS, SendGrid, etc.)
  • Encryption keys and signing keys
  • OAuth client secrets
  • SSH keys and TLS private keys
  • JWT signing secrets
  • Webhook signing keys

If an attacker had this value, could they access something they should not? If yes, it is a secret.

Where secrets should never be

Source code

The most common mistake. A secret committed to Git is compromised forever — even if you delete it in a later commit. Git history preserves everything.

# DO NOT DO THIS
STRIPE_SECRET_KEY = "sk_live_abc123def456"

Even in private repositories, this is a risk. Private repos get cloned to developer laptops, copied to CI runners, and occasionally made public by accident.

Unencrypted config files

.env files checked into version control, config.yaml with plaintext passwords, application.properties committed to the repo — all common and all dangerous.

.env files are acceptable for local development only and must be in .gitignore.

Client-side code

Anything in your JavaScript bundle, HTML source, or mobile app package is public. API keys in frontend code are visible to anyone who opens the browser's developer tools.

If a frontend needs to call a service that requires an API key, proxy the request through your backend.

Log files

Secrets accidentally logged are secrets compromised. Database connection strings in startup logs, API keys in debug output, tokens in request/response logs — all common patterns.

# BAD — logs the full request including Authorization header
logger.info(f"Request: {request.headers}")

# GOOD — omit sensitive headers
safe_headers = {k: v for k, v in request.headers.items() if k.lower() != "authorization"}
logger.info(f"Request headers: {safe_headers}")

Error messages

Stack traces and error messages that include secrets are a common leak vector, especially in development mode.

Where secrets should be

Environment variables

The simplest approach for many applications:

export DATABASE_URL="postgres://user:password@host:5432/db"
import os
db_url = os.environ["DATABASE_URL"]

Limitations:

  • Available to every process in the same environment
  • Often visible in process listings (/proc/*/environ on Linux)
  • No access control, auditing, or rotation support
  • Good enough for simple deployments; insufficient for larger systems

Secrets managers

Dedicated secrets storage services:

ServiceProvider
AWS Secrets ManagerAWS
GCP Secret ManagerGCP
Azure Key VaultAzure
HashiCorp VaultSelf-hosted / cloud
DopplerThird-party

Benefits:

  • Access control (IAM policies determine which services can read which secrets)
  • Audit logging (who accessed which secret, when)
  • Automatic rotation (for supported services like database passwords)
  • Versioning (roll back if a rotation fails)
  • Encryption at rest and in transit

Example: AWS Secrets Manager

import boto3
import json

client = boto3.client("secretsmanager")
response = client.get_secret_value(SecretId="myapp/database")
secret = json.loads(response["SecretString"])
db_password = secret["password"]

Example: HashiCorp Vault

vault kv get -field=password secret/myapp/database

Secret rotation

Secrets should be rotated regularly and immediately after any suspected compromise.

Automated rotation

Cloud secrets managers can automatically rotate secrets for supported services:

  • AWS Secrets Manager can rotate RDS passwords automatically using Lambda functions.
  • GCP Secret Manager can trigger rotation via Pub/Sub events.
  • Vault supports dynamic secrets — generating short-lived, unique credentials on demand.

Designing for rotation

Your application must handle rotation gracefully:

  • Do not cache secrets indefinitely. Re-read secrets periodically or on failure.
  • Support dual credentials. During rotation, both the old and new credential should work briefly. Database services and API providers typically support this.
  • Test rotation. Rotate secrets in staging before production. A failed rotation in production means downtime.

Secret detection

Prevent secrets from leaking into source control:

Pre-commit hooks

Tools that scan staged files before they are committed:

  • gitleaks — fast, configurable, supports custom patterns
  • detect-secrets (Yelp) — generates a baseline and flags new secrets
  • truffleHog — scans commit history for high-entropy strings
# Install gitleaks as a pre-commit hook
gitleaks detect --source . --verbose

CI/CD scanning

Run secret detection in your CI pipeline as a second line of defence:

# GitHub Actions example
- name: Scan for secrets
  uses: gitleaks/gitleaks-action@v2

Repository scanning

GitHub, GitLab, and Bitbucket offer built-in secret scanning that alerts you when known secret patterns (API keys for specific providers) are pushed.

Revoking compromised secrets

When a secret is compromised:

  1. Revoke immediately. Do not "monitor" a compromised secret. Revoke it.
  2. Rotate to a new value. Generate a new credential and deploy it.
  3. Audit usage. Check logs for unauthorised access using the compromised secret.
  4. Investigate the root cause. How did it leak? Fix the process, not just the symptom.

Most cloud providers and SaaS platforms provide APIs to revoke and regenerate API keys instantly. Automate this process.

Secrets in development

  • Use .env files (in .gitignore) for local development.
  • Use different secrets for development and production. Never share production credentials with development environments.
  • Consider tools like direnv or dotenv that load environment variables per-project.
  • For teams, use a shared secrets manager (Vault, Doppler, 1Password for Teams) instead of passing secrets through Slack or email.

Summary

Never put secrets in source code, logs, error messages, or client-side code. Use environment variables for simple deployments and dedicated secrets managers for production. Rotate secrets regularly and design your application to handle rotation gracefully. Use pre-commit hooks and CI scanning to catch leaks before they reach the repository. When a secret is compromised, revoke first, investigate second.


This training content is AI-assisted and reviewed by our team, but issues may be missed and best practices evolve rapidly. Send corrections to [email protected].