Intermediate Application & Code

Secrets management — never in code, vaults (Vault, Secrets Manager)

Secrets are any data that grants access to a resource: passwords, API keys, tokens, certificates, private keys. When a secret leaks, the attacker has direct access to the protected resource — database, payment service, cloud account. The most common leak is still the most avoidable: hardcoded values committed to a repository.

Why Never Hardcode Secrets

# Bad — hardcoded secret
DATABASE_URL = "postgresql://admin:S3cr3t@db.example.com/prod"
API_KEY = "sk-live-abc123xyz"

# Commit + push = secret exposed forever in git history

Git stores the full history. Even if you delete the secret in a later commit, it remains accessible via git log or GitHub history. Tools like truffleHog and gitleaks constantly scan public and private repositories looking for exactly this.

Correct Alternatives

1. Environment Variables

The simplest solution for local development and small projects:

# .env (never commit — add to .gitignore)
DATABASE_URL=postgresql://admin:S3cr3t@db.example.com/prod
API_KEY=sk-live-abc123xyz
import os

db_url = os.environ["DATABASE_URL"]   # raises explicit error if absent
api_key = os.getenv("API_KEY", "")    # empty default — use with care

Limitation: local .env does not scale to multiple services or secret rotation.

2. HashiCorp Vault

Vault is an open-source secret vault with granular access control, audit logging, and automatic rotation.

# Write a secret to Vault
vault kv put secret/myapp/db \
  url="postgresql://admin:S3cr3t@db.example.com/prod" \
  password="S3cr3t"

# Read at deploy / runtime
vault kv get -field=url secret/myapp/db
import hvac

client = hvac.Client(url="https://vault.example.com", token=os.environ["VAULT_TOKEN"])
secret = client.secrets.kv.read_secret_version(path="myapp/db")
db_url = secret["data"]["data"]["url"]

The application never loads the secret from a file — it fetches from Vault at runtime using a short-lived token.

3. AWS Secrets Manager

For AWS workloads, Secrets Manager integrates with IAM for access control and supports automatic rotation:

import boto3, json

def get_secret(secret_name: str) -> dict:
    client = boto3.client("secretsmanager", region_name="us-east-1")
    response = client.get_secret_value(SecretId=secret_name)
    return json.loads(response["SecretString"])

db_creds = get_secret("prod/myapp/db")
conn = psycopg2.connect(
    host=db_creds["host"],
    user=db_creds["username"],
    password=db_creds["password"],
)

The EC2/ECS instance uses an IAM role with minimal permissions — no credentials in the code or container.

Other Vaults

PlatformService
GCPSecret Manager
AzureKey Vault
GitHubActions Secrets
KubernetesSecrets (+ external-secrets operator)

Leak Detection

Enable in your repository:

# gitleaks — scans git history
gitleaks detect --source . --verbose

# truffleHog — detects high entropy strings (possible keys)
trufflehog git file://. --only-verified

On GitHub, enable Secret Scanning in repository settings — GitHub blocks pushes matching known API key patterns.

Secret Rotation

A secret that never changes is a permanent risk. Define:

  • Periodic rotation (e.g., 90 days for API keys, 30 for database passwords).
  • Immediate rotation when compromise is suspected.
  • Zero-downtime rotation: write new secret, update app, revoke old one.

Checklist

  • .env and config files with secrets in .gitignore
  • git log audited — no secrets in history
  • Secrets accessed via environment variables or a vault
  • Rotation configured and tested
  • Secret scanning enabled in the repository
  • No secrets in application logs