Skip to content

Secrets_management


Secrets are sensitive credentials that need special protection in your application.

What Counts as Secrets?
======================
┌─────────────────────────────────────────────────────────────┐
│ Authentication Credentials │
│ ─────────────────────────────────────────────────────────│
│ • Database passwords │
│ • API keys (AWS, Stripe, Twilio, etc.) │
│ • OAuth client secrets │
│ • Service account credentials │
│ • SSH private keys │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Cryptographic Materials │
│ ─────────────────────────────────────────────────────────│
│ • Encryption keys (symmetric) │
│ • Private keys (certificates) │
│ • Signing keys │
│ • HMAC secrets │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Application Secrets │
│ ─────────────────────────────────────────────────────────│
│ • Third-party API tokens │
│ • Integration passwords │
│ • Admin credentials │
│ • Seed data for applications │
└─────────────────────────────────────────────────────────────┘
BAD PRACTICE: Secrets in Code
============================
# config.js (COMMITTED TO GIT!)
─────────────────────────────────
const config = {
dbPassword: "my_secret_password_123", // 🚨 BAD!
apiKey: "sk_live_abc123def456", // 🚨 BAD!
stripeKey: "sk_live_xxx" // 🚨 BAD!
};
What happens:
1. Developer commits to git
2. Pushed to GitHub
3. Attacker finds in history
4. Complete system compromise!
─────────────────────────────────
Search GitHub for "password=" and find thousands of real secrets!

HashiCorp Vault Architecture
============================
┌─────────────────────────────────────────────────────────────┐
│ Applications │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ App 1 │ │ App 2 │ │ App 3 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
└───────┼──────────────┼──────────────┼──────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Vault Server │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Secrets │ │ Auth │ │ Audit │ │
│ │ Engine │ │ Methods │ │ Logs │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Storage Backend │
│ (Consul, etcd, DB) │
└─────────────────────────────────────────────────────────────┘
AWS Secrets Manager
=================
Features:
✓ Automatic rotation (Lambda)
✓ Fine-grained IAM access
✓ Encryption via KMS
✓ Cross-account access
✓ Integration with RDS, ECS, Lambda
─────────────────────────────────────────
Secret Structure:
{
"username": "admin",
"password": "secret-password",
"engine": "mysql",
"host": "db.example.com",
"port": 3306,
"dbname": "mydb"
}
FeatureHashiCorp VaultAWS Secrets ManagerAzure Key Vault
Self-hosted
Auto-rotation
Dynamic secretsLimited
EncryptionBring your ownKMSMHSM
CostOpen sourcePay per secretPay per operation

# Python with hvac (HashiCorp Vault client)
import hvac
import os
# Connect to Vault
client = hvac.Client(
url=os.environ['VAULT_ADDR'],
token=os.environ['VAULT_TOKEN']
)
# Read secret
secret = client.secrets.kv.v2.read_secret_version(
path='database/creds/myapp',
mount_point='secret'
)
db_password = secret['data']['data']['password']
# Use in connection
# connection = connect_db(password=db_password)
# AWS Secrets Manager with boto3
import boto3
import json
def get_db_credentials():
client = boto3.client('secretsmanager')
response = client.get_secret_value(
SecretId='prod/db/credentials'
)
secret = json.loads(response['SecretString'])
return secret['username'], secret['password']
# Read-only policy for application
path "secret/data/myapp/*" {
capabilities = ["read"]
}
# Read and write for deployment
path "secret/data/deploy/*" {
capabilities = ["read", "create", "update"]
}
# Admin policy
path "secret/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}

Secret Rotation Pattern
======================
┌─────────────────────────────────────────────────────────────┐
│ Before Rotation │
│ │
│ App uses: password_v1 │
│ DB stores: password_v1 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Rotation Process (triggered by schedule or event) │
│ │
│ 1. Generate new password (password_v2) │
│ 2. Update database with password_v2 │
│ 3. Update Vault with password_v2 │
│ 4. Invalidate password_v1 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ After Rotation │
│ │
│ App automatically retrieves new credentials │
│ (with short TTL or on next request) │
│ │
│ Uses: password_v2 ✓ │
└─────────────────────────────────────────────────────────────┘
# CloudFormation for RDS secret with rotation
Resources:
MyDBSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub 'rds-db-credentials-${DBInstanceName}'
Description: Credentials for RDS MySQL
GenerateSecretString:
SecretStringTemplate: '{"username": "admin"}'
GeneratePasswordKey: "password"
Tags:
- Key: "managed-by"
Value: "cloudformation"
MyDBInstance:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceIdentifier: mydb
MasterUserSecret:
SecretArn: !Ref MyDBSecret

Secrets Management Checklist
============================
✓ NEVER commit secrets to git
✓ Use a secrets manager (Vault, AWS Secrets Manager)
✓ Enable automatic rotation
✓ Use short TTL for dynamic secrets
✓ Implement audit logging
✓ Restrict access with IAM/policies
✓ Encrypt secrets at rest
✓ Use different secrets per environment
✓ Implement secret vending (dynamic creds)
✓ Regular security audits
─────────────────────────────────────────
Environment-Specific Secrets:
─────────────────────────────
Development:
• Shared credentials OK for local dev
• Use .env files (not committed)
Staging:
• Production-like secrets
• Rotation enabled
Production:
• Unique per service
• Automatic rotation required
• Audit everything
12-Factor: Config
================
Store config in the environment:
# .env (local, NOT committed)
DATABASE_URL=postgres://user:pass@localhost/db
API_KEY=sk_test_xxx
# In code:
import os
db_url = os.environ['DATABASE_URL']
─────────────────────────────────────────
For secrets, use:
1. Environment variables (for container orchestration)
2. Secrets manager (for centralized management)
3. Combined: Secrets manager + inject as env vars

  1. Never commit secrets - Use environment variables or secrets manager
  2. Centralize - Use HashiCorp Vault, AWS Secrets Manager
  3. Rotate - Automatic rotation is essential
  4. Dynamic secrets - Generate per-use credentials
  5. Audit - Log all access to secrets
  6. Encrypt - At rest and in transit

Next: Chapter 39: DDoS Protection