This guide shows practical checks, examples, and remediation snippets for common authentication flows (JWT, sessions, refresh tokens, password resets, rate limiting, etc.). Use it as a framework for security audits and for creating automated tests that validate each control.
Overview
- We used a detailed structured prompt to drive an automated security review of an Express app. The prompt enumerated specific checks and required the reviewer not to invent files or claims when context was missing.
- Goal: produce a concrete authentication security audit with structured findings (title, severity, evidence, remediation), tests, and minimally invasive fixes prioritized by impact.
Prompt context (excerpt)
The automated reviewer was instructed to “conduct a comprehensive authentication security review” and to check the controls summarized below. If the codebase lacks evidence for a claim, the reviewer must report “insufficient context” rather than fabricate files or behavior.Do not invent files, functions, or evidence. If an artifact cannot be located, the reviewer must explicitly state “insufficient context” for that check.
Authentication & session controls checklist
-
Password hashing
- Use bcrypt with salt rounds (recommendation: 12).
- Use async APIs (
bcrypt.hash, not sync variants); avoid double-hashing on updates. - Use
bcrypt.comparefor login verification.
-
Secret management (JWT and other keys)
- Never hard-code secrets; load them from environment variables or a secret manager.
- Use separate keys for access vs refresh tokens.
- Prefer high-entropy symmetric keys or an asymmetric key pair with rotation.
-
Token configuration
- Access token TTL: short (5–15 minutes recommended).
- Refresh token TTL: moderate (7–30 days recommended).
- Consider sliding sessions capped by a maximum session age.
-
Refresh-token implementation
- Rotate refresh tokens on each use: issue a new token and mark the previous as revoked.
- Detect refresh-token reuse and invalidate the associated session family.
- Store refresh tokens hashed at rest (e.g., SHA-256).
-
Session invalidation
- Invalidate issued tokens on password reset/change or explicit logout.
-
Brute force protection
- Rate-limit login, reset, and verification endpoints.
- Use progressive backoff per (username + IP) in a fast store (Redis recommended).
- Optionally require CAPTCHA after thresholds.
-
Account enumeration defenses
- Return generic errors and equal timing for “user not found” vs “bad password”.
- Add small jitter on failures to make timing attacks harder.
-
Password reset flow
- Use
crypto.randomBytes(32)(or equivalent) for reset tokens; store the hash and set a short TTL (≤ 15–30 minutes). - Tokens must be single-use and invalidated after successful reset.
- Throttle reset email sending and avoid logging the raw tokens.
- Use
-
Email verification
- Use server-side verification only with short-TTL one-time tokens.
- Do not allow verification state to be set through untrusted inputs (prevent mass assignment).
-
Injection and auth bypass
- Avoid user-controlled operators in NoSQL filters; sanitize queries.
- Use parameterized queries for SQL to prevent injection.
-
Authorization (AuthZ) integrity
- Deny-by-default access controls; load roles/permissions server-side.
- For sensitive operations, revalidate permissions against the database rather than relying only on JWT claims.
-
Cookie & CSRF configuration (if cookies used)
- Set
HttpOnly,Secure,SameSite(Lax/Strictas appropriate), explicitMax-Age, and narrowpath/domain. - Protect state-changing endpoints and token refresh routes from CSRF (double-submit token or ensure SameSite strategy).
- Set
-
Input validation & normalization
- Normalize emails/usernames; enforce length and charset checks.
- Enforce a strong password policy.
- Use schema validation libraries such as zod, joi, or celebrate.
-
Mass assignment risks
- Whitelist updateable fields; disallow clients from writing
role,emailVerified,passwordResetToken, etc.
- Whitelist updateable fields; disallow clients from writing
-
JWT misuse
- Never use
jwt.decode()for authorization decisions. Always usejwt.verify()with explicitalgorithms. - Prefer reputable libraries like
jsonwebtokenfor signing and verifying.
- Never use
-
Logging & telemetry
- Do not log passwords, tokens, reset links, or other secrets. Use structured logs with redaction/allowlists.
-
Dependency & crypto hygiene
- Keep crypto/auth libraries up to date; avoid weak hashes (MD5, SHA1) for password storage.
- Use Node’s
cryptocorrectly as per Node.js docs.
-
Transport & CORS
- Enforce HTTPS; restrict CORS to trusted origins (do not use wildcard credentials).
-
Open redirect /
nextparam- Restrict post-login redirection to vetted paths or origins; default to an approved fallback.
-
Operational controls
- Secret rotation procedures, environment separation, monitoring for suspicious auth activity, and alerts for refresh-token reuse.
Report format (recommended)
For each finding, provide:- Title
- Severity (Critical/High/Medium/Low)
- CWE mapping (if applicable)
- Evidence (file / function / line ranges)
- Why it matters (short)
- Exploitability notes (safe PoC where possible)
- Remediation: precise code-level fix or config change (include code snippets)
- Defense-in-depth guidance
- Summary risk score (0–10) and prioritized fixes
Example findings and remediation snippets
The automated analysis typically flags common issues in sample projects: placeholder JWT secret, missing password hashing on registration, excessive access-token TTLs, absent refresh-token logic, missing brute force protection, and insufficient validation.Weak JWT secret (example)
Evidence:Password hashing on registration and login
Use async bcrypt hashing and comparison:Brute force protection (rate limiter example)
Use an IP + username keyed fast store (Redis recommended). Example using express-rate-limit:Stronger password validation
Example using express-validator:Refresh token rotation & storage (high level)
Model refresh tokens in a database table with: hashed token, user_id, expiry, and session_id/family_id. On refresh:- Verify presented refresh token by comparing hashed value.
- If valid and unused: issue a new refresh token (rotation) and mark old as revoked.
- If reuse is detected: invalidate the entire session family and require re-authentication.
Password reset tokens
Create a cryptographically random token, store the hash at rest, set a short TTL, and invalidate after use:Account enumeration & timing defense
Return uniform messages for auth failures:SQL/NoSQL injection defenses
- SQL: use parameterized queries (example with pg):
- NoSQL: avoid allowing user-controlled operators in filters. Sanitize inputs and use allowlists.
Cookie & CSRF configuration (if using cookies)
Recommended cookie flags:HttpOnly: trueSecure: trueSameSite: 'Lax'or'Strict'depending on app flow- Explicit
maxAge, narrowpathanddomain
JWT advice recap
- Always call
jwt.verify()with explicit algorithms. Do not rely onjwt.decode()for authorization. - For critical authorization decisions, revalidate claims against server-side state (database) rather than trusting claims alone.
Logging and telemetry
- Redact or avoid logging PII, passwords, tokens, and reset links. Use structured logs and allowlists for safe fields.
Dependency & crypto hygiene
- Keep libraries up to date.
- Avoid custom crypto implementations for auth. Use Node’s
cryptoand vetted libraries.
Operational testing suggestions
Automate tests for:- Brute force protection (verify rate-limiter triggers).
- Refresh-token rotation and reuse detection (session revocation on reuse).
- Password reset token expiry and single-use behavior.
- JWT verification algorithm enforcement.
Example remediation: rate limiting and login validation (consolidated)
Not applicable or missing features (sample project)
- Password reset flow: Not implemented in the sample.
- Email verification: Not implemented.
- CSRF protection: Not applicable for APIs that do not use cookies, but must be considered if cookies are later introduced.
- CORS: No explicit configuration in sample; must be locked down for production.
Sample security checklist results (condensed)
Notes:
- Any examples in this table that contain code-like values (e.g.,
.env,24h,bcrypt.compare()) are shown in inline code to avoid parsing issues.
Operational remediation timeline (example)
- Immediately (48 hours): Replace JWT secret, implement bcrypt hashing on registration, add rate limiting for auth endpoints, reduce access-token TTL.
- Short term (1 week): Add refresh token rotation, detect token reuse, implement session invalidation.
- Medium term (1 month): Add password reset flow with secure tokens, email verification, CORS/HTTPS enforcement, and automated tests.
Executive summary (example output from automated audit)
- Risk Score: 8.5/10 (High Risk)
- Critical findings: weak JWT secret management, missing password hashing on registration, excessive access-token TTLs, missing refresh-token implementation, and no brute force protection.
- Prioritized immediate actions:
- Replace hard-coded JWT secret with a strong, rotated secret stored in a secret manager.
- Add password hashing on registration using bcrypt (async, adequate salt rounds).
- Implement rate-limiting for auth endpoints (login, reset).
- Reduce access token lifetime and implement refresh-token rotation with reuse detection.
- Add logging redaction and robust input validation.
Common gotchas for automated reviewers
- Do not invent files or functions. If context is missing, report “insufficient context”.
- Prefer to point to exact files/line ranges when making evidence-based findings.
- Always provide minimal reproducible remediation examples and tests.
- The structured prompt yields a concrete remediation plan and prioritized checklist. Immediate mitigations with the best ROI are secret/key hygiene, password hashing, short-lived access tokens + refresh token rotation, and brute force protections.
