Skip to main content
In this lesson we audit application business logic — the sequence of operations, rules, and state transitions that define how an application behaves as users interact with it. Business-logic vulnerabilities are often subtle and can enable attackers to bypass intended workflows, manipulate values, or expose sensitive information.
A presentation slide titled "Business Logic Vulnerabilities" with a dark blue curved panel on the right containing the word "Demo." The bottom left shows a small "© Copyright KodeKloud" notice.
What is business logic?
  • Business logic implements application functionality: login/logout, purchases, transfers, approvals, workflows, and the rules that guard them.
  • Small applications can still expose serious business-logic flaws (for example: user enumeration via timing differences or missing brute-force protections in a login flow).
  • When auditing, focus on where server-side enforcement is missing, where state transitions are inconsistent, or where client-supplied values are treated as authoritative.
Sample interactive prompt that appears in the project context:
* Welcome to Claude Code!

/help for help, /status for your current setup

cwd: /Users/jeremy/Repos/Claude Code Course/Express-login-demo

> Try "create a util logging.py that..."

? for shortcuts

In SESSION_COOKIE_SECURITY_AUDIT.md
Audit assistant prompts and repo Below is the auditing prompt we drop into the assistant to drive this business-logic review (trimmed for clarity):
Exploitability notes and, where safe, a minimal PoC or reproduction steps (no real secrets).

Remediation: precise code-level fix or config change (snippets welcome), plus defense-in-depth guidance.

A summary risk score (0–10) and top 3–5 prioritized fixes that reduce risk fastest.

A checklist diff: which items from the “Check for” list are Pass/Fail/Not Applicable.

## Constraints & style:

Be concrete and cite exact code locations and identifiers.

Prefer minimal, drop-in fix snippets over prose.

Do not invent files or functions that aren't present; if context is missing, mark as Unable to verify and say what code would prove it.

Write this into a markdown file and place it in the audits/ folder.
Primary business-logic issue classes to check
  • Race conditions: concurrent request handling, double-spending prevention, inventory updates.
  • Price and value manipulation: client-side totals, coupon/discount abuse, currency tampering.
  • Workflow bypass: skipping validation steps, status manipulation, approval-flow circumvention.
  • Time-based vulnerabilities: TOCTOU (Time of Check, Time of Use), expiration bypasses, timezone issues.
  • Integer overflow/underflow: calculation errors and negative-value handling.
Table — Issue classes, what to check, and examples
Issue classWhat to verifyExample
Race conditionsAtomicity of state updates, DB transactions, optimistic/pessimistic locksTwo simultaneous checkouts decrement same inventory item
Price manipulationServer-side price calculation and validationClient submits modified total or discount code directly
Workflow bypassEndpoint sequencing, required status checksCalling approval endpoint directly to mark request as approved
Time-based issuesNormalize time comparisons, use server time for expiry checksUsing client time to validate a token expiry
Integer overflowUse proper numeric types and bounds checksBalance underflow when subtracting large values
Common examples and short explanations
  • Double-spend / race conditions: Concurrent requests modify shared state (e.g., inventory) without suitable locking or transactional safeguards.
  • Client-side price validation: Never trust totals or prices from the client; compute and validate authoritative values server-side.
  • Workflow bypass: Ensure each step in a workflow enforces required checks and transitions; attackers can call endpoints out-of-order to override intended flow.
  • Time-based attacks: Differences between time of check and time of use may allow expiry bypasses or TOCTOU issues.
  • Integer issues: Watch for underflow/overflow in financial calculations, counters, and balances.
Always validate critical values on the server — client-side checks are for better UX only and can be bypassed by an attacker.
Example: audit output produced by the tool
Write(audits/BUSINESS_LOGIC_VULNERABILITY_AUDIT.md)
└ Wrote 203 lines to audits/BUSINESS_LOGIC_VULNERABILITY_AUDIT.md
**Application:** Express Login Demo
**Date:** 2025-08-21
**Scope:** Authentication flow, user management, and business logic security

## Executive Summary
**Risk Score: 6.5/10 (Medium-High)**
Key findings (summary)
  • Risk Score: 6.5/10 — five vulnerabilities identified; rate limiting and timing attacks are top priority.
  • Top findings:
    1. Timing attack / user enumeration via measurable response-time differences.
    2. Missing rate limiting (no brute-force protection on login endpoint).
    3. Weak JWT secret present in development configuration.
    4. Error information disclosure (detailed DB errors returned).
    5. Missing account lockout / failed-attempt tracking.
Detailed example — Timing attack (user enumeration)
  • The audit flagged a measurable timing discrepancy in the authentication path. When a login attempt references a non-existent user, the code returns immediately. When a user exists, the code calls bcrypt.compare(), which adds a measurable delay (~100 ms). Attackers can distinguish valid accounts by measuring response times across many requests.
Evidence (excerpted from routes/auth.js:29-46):
// Line 29-30: Database lookup
const userResult = await pool.query(userQuery, [email]);

// Line 32-36: Early return if user not found
if (userResult.rows.length === 0) {
  return res.status(401).json({ error: 'Invalid credentials' });
}

// Line 40: Password comparison only if user exists
const isPasswordValid = await bcrypt.compare(password, user.password);
Why it matters
  • Early return for non-existent users vs. bcrypt.compare() for existing users produces measurable timing differences.
  • Attackers can enumerate valid accounts by measuring average response times.
Remediation — normalize timing by always performing a password hash comparison, using a dummy hash when the account is not found:
// Perform lookup
const userResult = await pool.query(userQuery, [email]);
const user = userResult.rows[0];

// Dummy bcrypt hash (cost appropriate for your environment)
// You can generate a dummy hash once and store it in config; use the same hash for timing normalization.
const DUMMY_HASH = '$2b$10$C6UzMDM.H6dfI/f/IKcDReW1Z8G9YfnmY0g8b6KQ/7Qk7Yb1r6l6a'; // example bcrypt hash

// Use the real password hash if user exists, otherwise the dummy hash.
// This prevents response time differences between “user exists” and “user does not exist”.
const hashToCompare = user ? user.password : DUMMY_HASH;
const isPasswordValid = await bcrypt.compare(password, hashToCompare);

if (!isPasswordValid) {
  // Return the same generic error for both non-existent users and bad passwords.
  return res.status(401).json({ error: 'Invalid credentials' });
}
Brute-force protection — add rate limiting
  • The login endpoint lacked rate limiting. Add express-rate-limit or a similar middleware to slow or block brute-force attempts.
Example using express-rate-limit:
const rateLimit = require('express-rate-limit');

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit to 5 requests per window per IP (adjust as needed)
  message: { error: 'Too many login attempts, please try again later' },
  standardHeaders: true,
  legacyHeaders: false,
});

router.post('/login', loginLimiter, [/* validation middleware */], async (req, res) => {
  // existing login handler
});
Account lockout / failed-attempt tracking
  • Implement server-side counters and temporary lockouts after repeated failures. Use business-appropriate backoff and unlock mechanisms (email or admin unlock) to avoid permanent denial for legitimate users.
Weak JWT secret
  • Do not use default or development secrets in production. Generate secure secrets and fail-fast if missing.
Generate a strong secret:
openssl rand -base64 64
Enforce JWT secret presence/strength at startup:
if (!process.env.JWT_SECRET || process.env.JWT_SECRET === 'your_jwt_secret_key_here') {
  throw new Error('JWT_SECRET must be set to a secure random value');
}
Error information disclosure
  • Avoid returning detailed database or stack traces to clients. Log internal details server-side and return a concise, generic error to the caller.
Example error handling pattern:
switch (error.code) {
  case 'ECONNREFUSED':
    // internal logging
    console.error('Database error:', error.code, error.message);
    return res.status(503).json({ error: 'Service temporarily unavailable' });
  case '28P01':
    console.error('Database authentication failed:', error.code);
    return res.status(503).json({ error: 'Service temporarily unavailable' });
  default:
    console.error('Database error:', error);
    return res.status(500).json({ error: 'Internal server error' });
}
Checklist results (from the audit)
CheckResult
Race conditionsPass
Price manipulationNot applicable
Workflow bypassPass
Time-based vulnerabilitiesFail (timing attack / user enumeration)
Integer overflow/underflowNot applicable
Rate limitingFail
Account lockoutFail
Error information disclosureFail
Top 5 prioritized fixes
  1. Add rate limiting on authentication endpoints.
  2. Normalize authentication timing to mitigate user enumeration.
  3. Validate and require a strong JWT secret; fail startup if missing.
  4. Implement account lockout / failed-attempt tracking with safe unlock paths.
  5. Sanitize error responses to avoid leaking internal details.
Do not use development default secrets in production. Rotate weak secrets and enforce secure values in deployment pipelines.
Summary
  • Business-logic vulnerabilities can meaningfully increase application risk even in small demos.
  • In this Express login demo the primary issues were timing-based user enumeration and missing brute-force protections; both can be fixed with the code snippets above.
  • After applying fixes, re-run the audit to confirm mitigations and detect regressions.
Recommended next topic: secrets management and safe secret injection into CI/CD pipelines. Links and references

Watch Video