Insecure Design Testing: How to Find OWASP A04 Vulnerabilities

OWASP A04: Insecure Design is the newest category in the Top 10, introduced in the 2021 edition to capture a class of vulnerability that existing categories kept missing. It's not about a missing input validation check or a misconfigured HTTP header. It's about flaws that are baked into how an application was designed — flaws that exist before a single line of code is written, and that can't be patched away after the fact.

That distinction matters enormously for testing. Every other OWASP category targets implementation bugs: code that was written incorrectly. Insecure design targets intent: logic that was conceived incorrectly, or never conceived at all. You can fix an XSS vulnerability by adding output encoding. You cannot fix a fundamentally broken business workflow by adding a WAF rule.

This guide covers how to test for insecure design systematically — the vulnerability patterns to look for, how to probe them, what automated tools can and can't detect, and how to think about scope when the "bug" is the design itself.

What Insecure Design Actually Covers

The category is broad by intent. OWASP defines it as "missing or ineffective control design" — the absence of security controls at the design stage, as opposed to broken controls at the implementation stage. In practice, that maps to several distinct vulnerability patterns:

Business logic flaws The application's core workflows can be abused in ways that violate the intended business rules. Examples: purchasing items at a negative price, stacking discount codes beyond their intended limits, manipulating quantities or totals in ways the server doesn't validate, bypassing approval workflows by forcing state transitions.
Missing rate limiting No controls on how many times a user (or an attacker) can invoke a sensitive operation. Login endpoints that allow unlimited password guesses. Password reset flows that allow unlimited OTP retries. Registration endpoints that allow unlimited account creation. SMS or email sending endpoints that can be abused to bomb a target.
Workflow bypass Multi-step flows — checkout, identity verification, payment — where individual steps aren't validated in sequence. An attacker can skip the payment step and go directly to order confirmation. Or complete step 3 without having completed step 1, because the server doesn't track flow state server-side.
Trust boundary violations The server trusts input that should be authoritative server-side. Price sent in the request body instead of looked up from the database. Discount percentage supplied by the client. Role or permission level included in a form field that the server accepts without verification. The design places trust on the wrong side of the boundary.
Predictable resource locations and sequential IDs Resources are assigned sequential or guessable identifiers — order IDs, invoice numbers, user IDs — that make enumeration trivial. Combined with weak access control this becomes IDOR, but the root cause is in the design: using auto-increment integers where opaque identifiers should be used.
Insufficient anti-automation controls No CAPTCHA, no device fingerprinting, no behavioral analysis on operations that are trivially automatable. Account creation without any friction. Coupon code brute-forcing. Gift card balance enumeration. The design assumes good-faith human interaction and provides no mechanism to distinguish it from automated abuse.

Testing Business Logic Flaws

Business logic testing requires understanding what the application is supposed to do before you can test what it actually does. There's no payload list for business logic — you're looking for gaps between the intended behavior and the actual behavior.

Step 1: Map the application flow

Before touching a proxy, document the workflows you're going to test. For an e-commerce application: what is the path from item selection to order completion? What state transitions happen at each step? What data is passed between steps? What validation is described in the spec — or, if there's no spec, what validation does a reasonable developer assume is present?

Specifically note:

Step 2: Test numeric value manipulation

For every numeric value that passes through the client, test what happens when you supply unexpected values:

POST /api/cart/add HTTP/1.1
Host: shop.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...

{
  "product_id": "PRD-8821",
  "quantity": -1,
  "unit_price": 49.99
}

# What does a negative quantity do?
# Does the server accept a client-supplied unit_price?
# What happens if quantity * price results in a negative total?

These aren't edge cases — they're direct probes of whether the server validates business rules server-side or defers to whatever the client sends.

Step 3: Test coupon and discount stacking

Promotion logic is a consistent source of business logic flaws. Test:

POST /api/checkout/apply-coupon HTTP/1.1
Host: shop.example.com
Content-Type: application/json

{"coupon": "SAVE20", "order_id": "ORD-44512"}

# Apply once — legitimate 20% discount applied
# Apply again immediately — does the discount double?

POST /api/checkout/apply-coupon HTTP/1.1

{"coupon": "SAVE20", "order_id": "ORD-44512"}

HTTP/1.1 200 OK
{"discount_total": -39.98, "order_total": 0.02}

# Coupon applied twice. Order total: €0.02.

Step 4: Test order state bypass

Can you force an order into a terminal state (confirmed, shipped) without completing prerequisite steps? Capture the request that transitions an order from "payment complete" to "confirmed" and replay it against an order that hasn't been paid. If the server only checks that the order ID is valid — not that it's in the correct prior state — the workflow is bypassable.

Testing Rate Limiting

Rate limiting tests are straightforward to run but the results require interpretation. A 429 Too Many Requests response means rate limiting exists. Anything else on the 20th, 50th, or 100th consecutive attempt suggests it doesn't.

Login endpoint probing

# Probe login endpoint with sequential password attempts
for i in $(seq 1 100); do
  curl -s -o /dev/null -w "%{http_code}\n" \
    -X POST https://app.example.com/api/auth/login \
    -H "Content-Type: application/json" \
    -d "{\"email\":\"target@example.com\",\"password\":\"attempt${i}\"}"
done

# If all responses are 200 or 401 (never 429):
# No rate limiting on the login endpoint.

# Also test: does the account lock after N failures?
# Does the lockout apply per-IP, per-account, or both?
# Can you bypass per-IP lockout with X-Forwarded-For header manipulation?

Password reset and OTP probing

Password reset flows that send OTPs are especially sensitive. A 6-digit numeric OTP has 1,000,000 possible values. Without rate limiting, an attacker can brute-force it in under an hour. Test:

POST /api/auth/verify-otp HTTP/1.1
Host: app.example.com
Content-Type: application/json

{"email": "target@example.com", "otp": "000001"}

# Automate through 000001 → 999999
# No rate limit = account takeover via OTP brute force
# Weak rate limit (e.g., 100 attempts) = still exploitable with targeted guessing

Registration and SMS bombing

Registration endpoints that send verification emails or SMS messages without rate limiting can be used to spam arbitrary recipients. Test whether you can trigger 50 verification messages to the same phone number or email address in rapid succession. The damage here isn't to the application — it's to the recipient.

Testing Workflow Bypass

Multi-step flows are secure only if the server enforces step ordering and validates completion of each step before advancing. Client-side enforcement — JavaScript that prevents navigating to step 3 before completing step 2 — is not security. It's a UI convenience that any attacker ignores by talking directly to the API.

Map the flow, then skip steps

Intercept and record every API request made during a complete legitimate flow. Then replay only the final request — the one that produces the terminal outcome (order placed, account verified, payment confirmed) — without the preceding requests.

# Full legitimate checkout flow:
# 1. POST /api/cart/checkout    → creates order, returns order_id
# 2. POST /api/payment/initiate → initiates payment session
# 3. POST /api/payment/confirm  → processes payment, returns payment_token
# 4. POST /api/orders/confirm   → confirms order, triggers fulfillment

# Bypass test: skip steps 2 and 3, jump directly to step 4
POST /api/orders/confirm HTTP/1.1
Host: app.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...

{"order_id": "ORD-44512"}

# Does the server check that a payment_token exists for this order?
# Does it verify payment status in the database, or trust that
# reaching this endpoint implies prior steps completed?

Replay completed steps out of sequence

Also test replaying requests from a completed flow against a new, unrelated order or session. Can a payment token from one transaction be applied to confirm a different order? Can a verification token from one account be reused on another?

Testing Trust Boundary Violations

Trust boundary violations are cases where the server accepts authoritative data from the client that it should be computing or looking up itself. The most common form is price manipulation: the client sends a price field that the server uses instead of looking up the product's actual price.

Identify client-controlled authoritative fields

Scan your captured requests for fields that should be server-authoritative. Common candidates:

Modify and replay

# Original request
POST /api/checkout HTTP/1.1
Host: app.example.com
Content-Type: application/json

{
  "items": [{"product_id": "PRD-8821", "quantity": 1}],
  "unit_price": 149.00,
  "total": 149.00,
  "currency": "EUR"
}

# Modified request — supply a different price
POST /api/checkout HTTP/1.1
Host: app.example.com
Content-Type: application/json

{
  "items": [{"product_id": "PRD-8821", "quantity": 1}],
  "unit_price": 1.00,
  "total": 1.00,
  "currency": "EUR"
}

# If the server processes the order at €1.00:
# The price is not verified against the product catalog server-side.
# Classic trust boundary violation.

Client-side validation bypass

Applications that rely on client-side validation — JavaScript form validators, UI controls that prevent certain inputs — require no special tooling to bypass. Any request intercepted in a proxy can be modified before it reaches the server. Test every field that a client-side control restricts by removing that restriction at the transport layer:

What Automated DAST Tools Catch vs. Miss

Insecure design is the OWASP category where the gap between automated scanning and manual testing is largest. The table below reflects what you should realistically expect from a DAST tool:

Pattern Automated detection Why
Missing rate limiting on login Good Probe an endpoint N times and check for 429 — mechanical and reliable
Missing rate limiting on password reset / OTP Good Same probing approach; scanners can target common reset endpoint patterns
Sequential / predictable resource IDs Moderate Detectable if scanner observes sequential IDs in responses; harder in API responses not linked from HTML
Client-supplied price fields accepted Limited Scanner can modify numeric fields, but confirming a "finding" requires understanding whether the value was actually used
Workflow step bypass Limited Scanner would need to model the intended flow sequence — that requires application-specific knowledge
Coupon stacking / promotion abuse Poor No generic heuristic can distinguish "discount applied once" from "discount applied twice" without understanding promotion rules
Complex business logic flaws Very limited Requires understanding what the application is supposed to do — by definition outside what a generic scanner can model

The fundamental constraint is that automated tools test for deviations from technical norms. Business logic flaws are deviations from application-specific business rules. A scanner cannot know that applying the same coupon twice is wrong — that knowledge lives in the product spec, not in any technical specification the scanner can parse.

Rate limiting is the exception: it's an absence of a technical control, detectable without understanding application intent. Everything else requires a tester who understands what the application is trying to do.

How Ironimo Approaches Insecure Design Detection

A DAST scanner that only runs generic probes will miss most of A04. Ironimo's approach combines automated coverage of the mechanically detectable patterns with structured tests for the most common business logic vulnerability classes:

Complex business logic — multi-variable promotion rules, approval chain bypasses, industry-specific workflow constraints — still requires manual review. No automated tool will replace a tester who has read the product requirements. What automation does is surface the mechanical gaps quickly so manual testing time goes toward the logic that actually requires human judgment.

Key Takeaways

Insecure design is the hardest OWASP category to remediate because the fix often requires re-architecting a workflow, not patching a function. A few principles that should guide both testing and remediation:

Ironimo scans for insecure design patterns — missing rate limiting, numeric field manipulation, sequential ID exposure, header-based throttle bypass — using the same Kali Linux toolset professional pentesters run. Results include the exact request that reproduced the finding and a clear remediation path.

Schedule on-demand scans or run them on a recurring cadence as your application evolves. Because business logic changes with every deployment.

Start free scan
← Back to blog