Skip to main content
This article summarizes a focused business-logic security audit of a simple Express-based login application. It covers how core auth flows execute, the most likely security gaps discovered, evidence, and precise, minimal code-level remediations you can drop into your codebase to reduce risk rapidly. Below is an example CLI prompt the audit tool prints while running an automated audit:
I copied a large audit prompt into the tool and let it run. You can find the prompts used for these audits at https://github.com/JeremyMorgan/claude-code-reviewing-prompts.
A dark-themed desktop screenshot showing a browser window open to a GitHub repository called "Claude-Code-Reviewing-Prompts." The browser sits over a code editor/IDE with a project file tree visible on the left.
The audit prompt instructed the tool to look for common business-logic issues, including:
  • Race conditions
    • Concurrent request handling
    • Double-spending prevention
    • Inventory management
  • Price manipulation
    • Client-side price validation only
    • Discount/coupon abuse
    • Currency manipulation
  • Workflow bypass
    • Skipping validation steps
    • Status manipulation
    • Approval process bypass
  • Time-based vulnerabilities
    • TOCTOU (Time of Check, Time of Use)
    • Expiration bypass
    • Timezone manipulation
  • Integer overflow/underflow
    • Calculation errors
    • Negative value handling
The prompt requested a structured finding report for each issue (title, severity, CWE if applicable, evidence, why it matters) and practical remediation with exact code/config suggestions.

Key Findings (summary)

Other analyses:
  • Race conditions: Pass (no concurrent-update patterns detected in current auth flow)
  • Price/value manipulation: Not applicable (no financial logic)
  • Workflow bypass: Pass (linear auth flow with validation)
  • Integer overflow: Not applicable (no numeric calculations)

Concrete remediations (drop-in fixes)

Below are concise, targeted fixes for the most critical issues. These examples assume an Express app using bcrypt, JWTs, and optionally Redis for counters.

1) Normalize timing to prevent user enumeration

Always perform a password comparison even when the user does not exist. Precompute a dummy bcrypt hash and compare against it when lookup fails so timings match closely.
Note: Generate the dummy hash once using bcrypt.hash('random-string', saltRounds) and store it in config or env rather than computing it on every request.
Using a precomputed dummy hash ensures bcrypt.compare execution time is similar whether or not a user exists, mitigating timing-based user enumeration.

2) Add rate limiting to the login route

Use a middleware such as express-rate-limit to limit login attempts per IP or combined with account-based throttling.
For improved defense, combine per-IP limits with per-account throttling and implement exponential backoff on failures.

3) Implement account lockout / failed-attempt tracking

Track failed attempts per account with a persistent store (Redis recommended) and lock the account after a threshold. Ensure counters persist to survive restarts.
Integrate with login flow:
  • Check lock status (but still run bcrypt.compare against dummy hash when account/user missing or locked to avoid timing differences).
  • On failed login, call recordFailedAttempt.
  • On success, call clearFailedAttempts.
  • Redis: https://redis.io/

4) Sanitize and standardize error messages

Return generic authentication failures and avoid exposing stack traces or implementation details in production.
Log detailed diagnostics to server logs only (do not return them to clients). Use environment checks to ensure verbose stack traces are only enabled in development.

5) Enforce strong, environment-driven JWT secrets

Never hardcode secrets. Require a secure environment variable, validate its length, and fail fast if not present. Rotate using a secrets manager.
Store secrets in a manager such as AWS Secrets Manager or HashiCorp Vault and rotate periodically.
Do not use weak or hardcoded JWT secrets in production. Compromised secrets let attackers forge tokens and impersonate users.

Checklist Results (condensed)

Top prioritized fixes

  1. Normalize timing with a precomputed dummy bcrypt hash (prevent user enumeration).
  2. Add rate limiting to login endpoints (mitigate brute-force and amplify defenses).
  3. Implement account lockout with persistent counters and exponential backoff.
  4. Enforce strong JWT secrets from environment and use secret management.
  5. Remove verbose error responses in production; log diagnostics to internal logs only.

Final notes — next steps

These changes are small but highly effective at reducing the attack surface for an authentication service. After applying them:
  • Add logging and alerting for suspicious auth activity (sudden lockouts, IPs with many failures).
  • Consider multi-factor authentication (MFA), device reputation checks, and IP allow-listing for higher-value accounts.
  • Perform periodic audits and threat modeling for newly added functionality.
References and further reading

Watch Video