fix(security): reject placeholder/weak JWT secrets at startup

The env-validation module previously only checked that JWT_SECRET and
JWT_REFRESH_SECRET were _present_ — it accepted any value, including
known placeholders like "CHANGE_ME". This meant a developer could copy
.env.example verbatim and run the app with predictable, forgeable tokens.

Changes:
- Add FORBIDDEN_SECRET_VALUES blocklist (case-insensitive) with 23 common
  placeholder strings (CHANGE_ME, secret, password, test, etc.)
- Enforce minimum 32-character length for JWT secrets (NIST HMAC guidance)
- Export validateJwtSecret() for direct testing and reuse
- Update .env.example: replace "CHANGE_ME" with generation instructions
- Add 14 unit tests covering placeholder rejection, length enforcement,
  missing-var errors, and production-mode validation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-09 01:20:30 +07:00
parent 05651ba4c3
commit e89cd0ce84
4 changed files with 226 additions and 5 deletions

View File

@@ -54,10 +54,16 @@ CORS_ORIGINS=http://localhost:3000,http://localhost:3001
# -----------------------------------------------------------------------------
# JWT / Auth (REQUIRED — app will not start without these)
#
# SECURITY: Generate strong, random secrets (min 32 characters).
# openssl rand -base64 48
#
# Do NOT use placeholder values like "CHANGE_ME" — the app will reject them.
# Each secret must be unique and kept out of version control.
# -----------------------------------------------------------------------------
JWT_SECRET=CHANGE_ME
JWT_SECRET=<generate with: openssl rand -base64 48>
JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=CHANGE_ME
JWT_REFRESH_SECRET=<generate with: openssl rand -base64 48>
JWT_REFRESH_EXPIRES_IN=7d
# -----------------------------------------------------------------------------