# Business logic vulnerabilities: examples, testing and fixes

Source: https://penetrationtestservices.com/guides/business-logic-vulnerabilities/
Publisher: Excession Labs (penetrationtestservices.com)
Last updated: 2026-11-09

What business logic vulnerabilities are, why scanners miss them, examples in pricing, workflows and limits, how testers prove them and how to fix them.

> **Short answer:** Business logic vulnerabilities are flaws in the rules of an application, not in its code syntax. Examples: changing a price in the request, skipping a payment step, reusing a coupon, or beating a quota. Scanners miss them because the requests look valid. Testers find them by learning the intended workflow, then breaking its assumptions with test accounts.

A business logic vulnerability is a flaw in the rules of an application. The code runs as written, but the rules allow an outcome the business never intended. Examples: a price the client can change, a payment step the user can skip, or a coupon that works twice.

Scanners miss these flaws because every request is valid HTTP. Testers find them with test accounts, and teams fix them by moving each rule to the server. We cover them in [web application penetration testing](/web-application-penetration-testing/).

## What are common business logic vulnerabilities?

Most logic flaws come from one error: the server trusts something the client controls. The OWASP Web Security Testing Guide groups them in section 4.10, which covers data validation, forged requests, integrity checks, process timing, usage limits, workflow circumvention, misuse defenses, file uploads and payment functionality.

| Pattern | Example | What the server should do |
|---|---|---|
| Client-set price | `price=1.00` in the checkout request | Read the price from the catalog |
| Negative or huge quantity | `quantity=-5` lowers the total | Reject values outside a range |
| Workflow skip | Call `/order/confirm` without `/order/pay` | Check state before each step |
| Reused one-time value | Coupon, invite or reset code works twice | Mark it used in a transaction |
| Race condition | Two parallel redeems both succeed | Use an atomic update or lock |
| Limit bypass | Free plan limit checked only in the UI | Check the limit on the server |
| Role or state confusion | Cancelled user still calls an API | Check account state on each call |
| Rounding and currency | Many small refunds exceed the total | Cap by the order total in cents |

OWASP API6:2023 names a related risk: Unrestricted Access to Sensitive Business Flows. It covers flows that harm the business when automated, such as buying out stock or abusing referrals.

## Why do scanners miss business logic flaws?

Scanners match known bad patterns, and a logic flaw has no signature. A request with `quantity=-5` is well-formed. Only someone who knows the cart total must never fall below zero can call it a bug.

Scanners also work one request at a time, while logic flaws often need a sequence, two accounts or a timing window. A person must plan that sequence.

## How do testers find them?

Testers learn the intended workflow first. Then they list the assumptions behind each step and test each assumption.

1. Map the workflow. Walk the app as a normal user. Note every state: cart, paid, shipped, refunded, cancelled.
2. List the rules. Write what must always be true. Example: a refund never exceeds the amount paid.
3. List the assumptions. Ask what the server trusts: price, quantity, step order, user role, timing.
4. Break one assumption at a time. Change the value, skip the step, repeat the action, or run two requests together.
5. Check the result in the system of record. Look at the order total, the balance, the plan.
6. Run a control. Repeat the action in the normal way to show what the correct result looks like.

### Worked example: price tampering

> **Illustrative request and response:** This is a demonstration. No real customer system was tested. "Harbor Notes" is a fictional app, and the host is an example.com host.
> 
> The browser sends this checkout request. The tester changes `unit_price_cents` to `100` using a test account and test payment mode:
> 
> ```http
> POST /api/v1/checkout HTTP/1.1
> Host: shop.harbor-notes.example.com
> Authorization: Bearer 
> Content-Type: application/json
> 
> {"plan": "team", "seats": 5, "unit_price_cents": 100}
> ```
> 
> A vulnerable server accepts the price:
> 
> ```http
> HTTP/1.1 200 OK
> Content-Type: application/json
> 
> {"order_id": "o-5521", "total_cents": 500, "status": "paid"}
> ```
> 
> The control request with the real catalog price returns `total_cents: 4500`. The tester then reads order `o-5521` back and checks the billing record. The stored total is the proof, not the `200 OK`.
## How do testers handle race conditions and limits safely?

Race condition tests send a small number of parallel requests, such as two or three, against a test-owned resource. Examples are redeeming one coupon twice or withdrawing one balance twice. The tester then reads the final state.

State the bounds in the scope: how many requests, from which accounts, and what test data. Do not run floods on shared systems. For repeated-action limits such as login or SMS sends, see [rate limiting and credential stuffing](/guides/rate-limiting-and-credential-stuffing/).

## Which test cases should every app run?

Adapt this starting list to your product.

- [ ] Change price, discount, currency and tax fields in the request.
- [ ] Send zero, negative, decimal and very large quantities.
- [ ] Call later workflow steps without finishing earlier steps.
- [ ] Replay a completed step (confirm, redeem, accept invite).
- [ ] Use a one-time code twice, once from a second session.
- [ ] Run two parallel requests for a single-use action.
- [ ] Exceed a plan limit through the API.
- [ ] Use a feature after downgrade, cancellation or role removal.
- [ ] Apply a discount to an item the discount excludes.
- [ ] Refund more than the amount paid, in pieces.
- [ ] Access another user's order or invoice (an [IDOR test](/guides/idor-testing/)).

## How do teams fix business logic flaws?

Move each rule to the server and write it once. The server computes prices, totals and entitlements from its own data and treats client values as requests.

- State machines. Define the allowed transitions (cart to paid to shipped) and reject any other.
- Atomic updates. Mark a coupon or token as used in the same transaction that grants the benefit. Use a unique constraint or row lock.
- Invariants. Check rules at the end of each operation, for example `refunded = coupon.max_uses: abort
      if order.state != "cart": abort
      coupon.used_count += 1
      order.discount = coupon.value_for(order.items)   # computed server-side
    commit
```

## When should you test business logic?

Test it when you add a money flow, a new role, a trial or plan change, or an approval step. The OWASP Top 10:2025 lists Insecure Design as A06 (see the [OWASP Top 10 2025 guide](/guides/owasp-top-10-2025/)), and API6 covers business flow abuse (see the [OWASP API Security Top 10 guide](/guides/owasp-api-security-top-10/)).

## What does a business logic finding look like?

A logic finding names the broken rule and the request that broke it.

> **Illustrative finding write-up:** This is a demonstration. No real customer system was tested. The app is fictional.
> 
> | Field | Detail |
> |---|---|
> | Title | Checkout accepts a client-set unit price |
> | Label | Observed |
> | Rule broken | The order total must equal the catalog price times the quantity. |
> | Steps | Create a team plan order in test payment mode. Change `unit_price_cents` from 900 to 100 in the request. |
> | Result | The order is created and marked paid at 500 cents. The billing record, read back, shows 500 cents. |
> | Control that held | A negative seat count returns 400. |
> | Impact | Any user can buy a plan at a price they choose. |
> | Fix | Ignore the client price. Compute the total on the server from the catalog. |
## What should you tell testers about your rules?

Testers cannot guess your rules, so give them a short brief:

1. The main workflows: sign-up, purchase, upgrade, cancel, refund, invite, approve.
2. The roles and what each may do.
3. The numbers that must hold: totals, balances, quotas, seat counts.
4. The "never" list: what must never happen, in plain words.
5. Test payment modes, test coupons and test data they may use.
6. Any action that costs money or sends messages, with a limit.

Each line of the "never" list becomes a test case: "A refund never exceeds the amount paid" becomes a test that refunds in parts. The brief also feeds the scope for [web application penetration testing](/web-application-penetration-testing/).

## Questions

### Why can't automated scanners find business logic flaws?

A scanner does not know your rules. A request that sets a negative quantity or skips a step is valid HTTP. Only a person who knows that the cart must total the right amount can say that the result is wrong.

### Is a business logic flaw a design bug or a code bug?

Often both. OWASP lists Insecure Design as A06:2025 for flaws where the design lacks a control. A coding mistake can also break a good design. The fix is usually a server-side rule plus a test.

### How do testers avoid causing real financial harm?

They use test accounts, test payment modes and test data only. The scope states the limits, such as no real card charges and a maximum number of requests. Anything with a cost is agreed first.

### What information helps testers most?

A description of the intended workflows, the roles, the limits and the money flows. Tell testers what must never happen, such as a refund above the order total. That list becomes the test plan.

## Sources

- [OWASP WSTG: Business Logic Testing (4.10)](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/10-Business_Logic_Testing/)
- [OWASP WSTG-BUSL-01: Test Business Logic Data Validation](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/10-Business_Logic_Testing/01-Test_Business_Logic_Data_Validation/)
- [OWASP API Security Top 10 2023: API6 Unrestricted Access to Sensitive Business Flows](https://api-security.owasp.org/editions/2023/en/0x11-t10)
- [OWASP Top 10:2025 Introduction](https://top10.owasp.org/2025/0x00_2025-Introduction)
