Securing Credentials in Node.js Best Practices for API Keys, Secrets, and Passwords
Learn the best practices for securing credentials in your Node.js application from .env files and AWS Secrets Manager to IAM roles and key rotation.
Securing Credentials in Node.js
Credential security is critical. A leaked API key or database password can lead to data breaches, unauthorized access, and financial loss. This guide covers best practices.
Level 1: .env Files (Basic)
For development and small projects:
# .env (never committed) MONGODB_URI=mongodb+srv://user:[email protected]/db JWT_SECRET=random_64_char_string AWS_ACCESS_KEY_ID=AKIAXXXXXX AWS_SECRET_ACCESS_KEY=xxxxxx
Pros: Simple, universal, no extra cost Cons: Not encrypted, manual rotation, no audit trail
Level 2: AWS Secrets Manager (Production)
For production apps with many secrets:
npm install @aws-sdk/client-secrets-manager
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager"); const client = new SecretsManagerClient({ region: "us-east-1" }); const getSecrets = async () => { const response = await client.send( new GetSecretValueCommand({ SecretId: "devtinder/prod" }) ); return JSON.parse(response.SecretString); }; // Usage const secrets = await getSecrets(); const dbUri = secrets.MONGODB_URI; const jwtSecret = secrets.JWT_SECRET;
Pros: Encrypted, automatic rotation, audit trail, IAM integration Cons: Cost ($0.40 per secret/month + $0.05 per 10k API calls), AWS-only
Level 3: IAM Roles on EC2 (Best for AWS)
Instead of storing AWS credentials in .env, use IAM roles:
- Create an IAM role with SES, S3, etc. permissions
- Attach the role to your EC2 instance
- The AWS SDK automatically uses the role credentials
// No credentials needed uses the EC2 IAM role const sesClient = new SESClient({ region: "us-east-1" });
Pros: No credentials in code or .env, automatic rotation, least privilege Cons: Only works on EC2 (or ECS, EKS, Lambda)
Generating Strong Secrets
# Generate a random JWT secret (64 characters) node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" # Generate a random API key node -e "console.log(require('crypto').randomBytes(24).toString('base64'))"
Never use weak secrets like "password", "secret", or "mykey". Always use randomly generated strings.
Credential Security Checklist
- .env in .gitignore
- .env.example with placeholder values
- No secrets in code (always use process.env)
- No secrets in Git history (check with git log -p)
- No secrets in logs (don't console.log process.env)
- No secrets in error messages sent to clients
- Strong, random JWT secrets (64+ characters)
- Different secrets for development and production
- IAM roles on EC2 (instead of access keys)
- AWS Secrets Manager for production secrets
- Regular key rotation
- Restrict IAM policies (least privilege)
Common Credential Mistakes
1. Hardcoding Secrets in Code
// WRONG const jwtSecret = "mysecret123"; const dbUri = "mongodb://user:pass@localhost:27017/db"; // RIGHT const jwtSecret = process.env.JWT_SECRET; const dbUri = process.env.MONGODB_URI;
2. Committing .env to Git
# Check if .env was ever committed git log --all --full-history -- .env # If it was, rotate ALL secrets immediately # They are compromised
3. Logging Secrets
// WRONG console.log("Connecting to:", process.env.MONGODB_URI); // RIGHT console.log("Connecting to MongoDB");
4. Weak Secrets
// WRONG const jwtSecret = "secret"; const jwtSecret = "myjwtsecret"; const jwtSecret = "devtinder"; // RIGHT const jwtSecret = "a7f3b2c9d8e1f4a6b5c3d2e1f7a9b8c6d5e4f3a2b1c9d8e7f6a5b4c3d2e1f9a8";
5. Same Secret Across Environments
# WRONG: same JWT_SECRET in dev and prod # .env.development JWT_SECRET=abc123 # .env.production JWT_SECRET=abc123 # RIGHT: different secrets # .env.development JWT_SECRET=dev_secret_a7f3b2... # .env.production JWT_SECRET=prod_secret_d8e1f4...
The Takeaway
Credential security involves: using .env files (never commit to Git), generating strong random secrets (64+ chars), using AWS Secrets Manager for production, IAM roles on EC2 (no credentials in code), different secrets per environment, no secrets in logs or error messages, regular key rotation, and least-privilege IAM policies. If a secret is ever committed to Git, rotate it immediately.
Use .env files (never commit to Git), generate strong random secrets (crypto.randomBytes), use AWS Secrets Manager for production, use IAM roles on EC2 instead of access keys, use different secrets per environment, never log secrets, and rotate keys regularly.
A managed service that stores, encrypts, and rotates secrets. Store your database URI, JWT secret, API keys as a JSON secret. Retrieve with the AWS SDK. Costs $0.40 per secret/month. Provides automatic rotation, audit trail, and IAM integration.
Create an IAM role with the needed permissions (SES, S3, etc.), attach the role to your EC2 instance. The AWS SDK automatically uses the role's temporary credentials. No access keys in .env, automatic rotation, and least privilege.
Run node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" to generate a 64-character hex string. Never use weak secrets like 'password', 'secret', or 'mykey'. Always use randomly generated strings.
Rotate ALL secrets immediately (they are compromised). Remove .env from Git with git rm --cached .env. Add .env to .gitignore. Generate new secrets for everything (JWT, database password, API keys). Commit the fix. Consider using git filter-branch to remove from history.
Ready to master Node.js completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master Node.js
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

