Best practices for managing API keys, passwords, tokens, and other sensitive credentials
Secrets management is the practice of securely storing, accessing, and auditing sensitive credentials like API keys, passwords, certificates, and encryption keys. Poor secrets management is a leading cause of security breaches.
What are Secrets?
Secret Type
Examples
Risk if Exposed
API Keys
AWS access keys, Stripe keys
Unauthorized API access, billing fraud
Passwords
Database passwords, admin credentials
Data breach, system compromise
Tokens
OAuth tokens, JWTs, session tokens
Account takeover, impersonation
Certificates
TLS/SSL certs, signing keys
Man-in-the-middle, code signing abuse
Encryption Keys
AES keys, KMS keys
Data decryption, privacy breach
Connection Strings
Database URLs with credentials
Database access
The Golden Rule
Never store secrets in source code, configuration files, or anywhere that gets committed to version control.
❌ BAD: Secrets in Code
─────────────────────────────────────────────────────────────
const API_KEY = "sk_live_abc123xyz"; // NEVER DO THIS
const DB_PASSWORD = "MyP@ssw0rd123"; // NEVER DO THIS
✅ GOOD: Secrets from Environment
─────────────────────────────────────────────────────────────
const API_KEY = process.env.API_KEY;
const DB_PASSWORD = process.env.DB_PASSWORD;
Secrets Management Architecture
Secrets Management Flow
Secrets Storage Solutions
Cloud Secrets Managers
Service
Provider
Features
AWS Secrets Manager
AWS
Rotation, cross-account, RDS integration
Azure Key Vault
Azure
HSM support, RBAC, managed identity
Google Secret Manager
GCP
Versioning, IAM, automatic replication
HashiCorp Vault
Self-hosted/Cloud
Dynamic secrets, PKI, multi-cloud
AWS Secrets Manager Example
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManagerClient({ region: 'us-east-1' });
async function getSecret(secretName: string): Promise<string> {
const command = new GetSecretValueCommand({ SecretId: secretName });
const response = await client.send(command);
if (response.SecretString) {
return response.SecretString;
}
throw new Error('Secret not found');
}
// Usage
const dbPassword = await getSecret('prod/database/password');
import {
SecretsManagerClient,
RotateSecretCommand
} from '@aws-sdk/client-secrets-manager';
// Lambda function for rotation
export async function handler(event: {
SecretId: string;
ClientRequestToken: string;
Step: string;
}) {
const { SecretId, ClientRequestToken, Step } = event;
switch (Step) {
case 'createSecret':
// Generate new secret version
await createNewSecret(SecretId, ClientRequestToken);
break;
case 'setSecret':
// Update the service with new credential
await updateServiceCredential(SecretId, ClientRequestToken);
break;
case 'testSecret':
// Verify new credential works
await testNewCredential(SecretId, ClientRequestToken);
break;
case 'finishSecret':
// Mark new version as current
await finalizeRotation(SecretId, ClientRequestToken);
break;
}
}
Application Support for Rotation
// Design apps to handle credential refresh
class DatabaseConnection {
private pool: Pool;
private lastRefresh: Date;
async getConnection() {
// Check if credentials might have rotated
if (this.shouldRefreshCredentials()) {
await this.refreshCredentials();
}
try {
return await this.pool.connect();
} catch (error) {
// On auth failure, try refreshing credentials
if (isAuthError(error)) {
await this.refreshCredentials();
return await this.pool.connect();
}
throw error;
}
}
private async refreshCredentials() {
const newPassword = await getSecret('database-password');
this.pool = new Pool({ ...config, password: newPassword });
this.lastRefresh = new Date();
}
}
This section fulfills ISO 13485 requirements for control of records (4.2.4) and infrastructure (6.3), and ISO 27001 requirements for cryptography (A.8.24), access control (A.5.15), and privileged access management (A.8.18).