Business Logic Vulnerabilities: When Your App Works Exactly as Coded, and That's the Problem

A checkout page that accepted a negative quantity nearly emptied a client's stock without a single line of broken code. Business logic flaws are the bugs scanners can't find.

A client once showed us a checkout that let a customer order minus five units of a product. The page accepted it, the total went negative, and the system quietly credited the buyer. No server error. No crash. Every validation rule the developers had written was technically satisfied, because nobody had ever written a rule for a negative quantity in the first place.

That is the essence of a business logic vulnerability. The code does exactly what it was told to do. The flaw lives in what nobody thought to tell it. This article is about how those flaws get in, why scanners cannot see them, and how to find the ones hiding in your own systems.

Why the smartest people miss these

Before the mechanics, the psychology, because the psychology is why these bugs survive. Developers write code against a mental model of how a user behaves: a customer buys between one and ten items, a coupon is used once, a password reset follows its steps in order. Every validation rule is written against that imagined user.

Attackers do not share the mental model. They ask the question developers never asked: what happens if the imagined user does the opposite of what I assumed? That gap between the imagined user and any real user is where every business logic flaw lives. The best defence is not more rules. It is deliberately thinking like someone who has never read your assumptions, which is a skill you can practise.

Why scanners miss these entirely

Automated security scanners are excellent at finding injection, misconfiguration, and known vulnerable libraries. They are nearly blind to logic flaws, because a logic flaw looks like normal behaviour. The request is well-formed, the endpoint responds as designed, and the transaction completes. Nothing about the HTTP traffic looks suspicious.

Finding these bugs requires thinking the way an attacker thinks: what could a reasonable person do with this screen that the developers never anticipated?

The five patterns that cause most of the damage

Over years of reviewing business systems, the same handful of patterns keeps showing up.

  • Negative and fractional values. Quantity minus one. Price minus fifty. A decimal quantity of 0.02 on a food delivery app bought the full item for two percent of the price. Validate that numbers are positive and whole where they must be, and never trust a value the client sends.
  • Missing-field fallback. One registration system fell back to a free tier whenever the client simply omitted the paid-option field. Backends that treat "field absent" as "nothing to enforce" create free upgrades. Require the field and validate its content server-side.
  • Step skipping. Multi-step flows invite direct access to the final step. Password reset without the token, checkout without payment, verification without the code. Every step must be verified on the server, in order, with state that cannot be forged.
  • Race conditions. Two requests arrive at the same moment, both check "is this coupon already claimed?", both see no, and both claim it. Send a request twice in parallel and the check and the update are no longer atomic. Use database transactions or unique constraints to make double-claiming impossible by construction.
  • Client-trusted status. Any field the client can set that the server believes, such as a payment status or a role, is a vulnerability. The server must compute truth, not read it from the request.

A worked example: the coupon race

Let's make the race condition concrete, because it is the pattern people find hardest to believe. Imagine a referral program that credits both parties when a code is used. The code's server logic reads "has this code been claimed?" and, only if the answer is no, writes the claim.

Now send two identical requests at the same instant. Both read the code as unclaimed, because neither write has landed yet. Both proceed to write. Two claims, one code, and the referral bonus mints money out of thin air. Repeat across a hundred codes and the program is not a marketing cost, it is a printing press.

The fix is not to make the check faster. It is to make the state impossible to double-write: a unique constraint on the code in the database, or a transaction that locks the row while checking and writing. When the database itself refuses the second claim, no timing trick can beat it.

How to audit your own business logic

You do not need a security team to start. For each important flow in your system, ask five questions:

  1. What happens if I skip a step?
  2. What happens if I send a negative, zero, or absurdly large value?
  3. What happens if I do the same thing twice at the same time?
  4. What happens if I do step B before step A?
  5. Which fields in this request does the server trust without checking?

Each question is a test case. Run it against your staging environment and watch what the server does, not what the interface does. The interface will politely refuse most of these; the server is where the truth lives, and the server is what you are testing.

A practical audit in an afternoon

  1. List your money and identity flows. Payments, coupons, subscriptions, refunds, password resets, account linking. These are where logic flaws cost the most.
  2. Write the happy path for each, then delete a step and try again. Note which deletions succeed.
  3. Intercept a real request with a proxy and edit the numbers. Negative prices, zero quantities, changed statuses.
  4. Fire the same request twice in parallel against coupons, claims, and withdrawals.
  5. Write the fixes down before you forget: server-side validation, required fields, transactions, unique constraints, server-computed status.

Where these bugs hurt most

Logic flaws in payments, coupons, subscriptions, and account recovery are the ones that cost real money. A race condition on a referral program mints credit forever. A skipped verification step hands an account to a stranger. A negative quantity empties a warehouse. Prioritise the flows where money or identity changes hands, because that is where an attacker is looking too.

There is a second-order cost that is easier to miss: the reputational one. A customer who discovers your system quietly credited them a refund they did not earn, or let a stranger into an account, tells the story to everyone. Trust is the most expensive thing a logic flaw can damage, because it is the hardest to rebuild.

The bottom line

Business logic vulnerabilities are not exotic. They are the ordinary gaps between what a system was designed to do and what it will do when someone pokes it with an unexpected input. The fix is not a better scanner. It is asking the uncomfortable questions about your own workflows, and treating the answers as bugs to fix rather than edge cases to ignore. If your developers have never audited the business rules this way, assume something is waiting to be found.

If you would like an outside pair of eyes on your flows, this is the kind of review we run for clients before things go wrong. Send us a message and we'll walk your most important flows with you.