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:
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:
- Every numeric value the user supplies (price, quantity, discount, shipping cost)
- Every state the application transitions through (cart, checkout, payment, confirmed)
- Every identifier that appears in requests (order ID, cart token, session state)
- Every place where the client sends data that the server should already know
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:
- Applying the same coupon code multiple times in a single order
- Applying multiple coupon codes simultaneously when the intent is one-per-order
- Applying a coupon code after a discount has already been applied
- Combining percentage discounts in ways that result in negative prices
- Reusing a "single use" coupon code after an order is cancelled
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:
price,unit_price,total,subtotalin checkout requestsdiscount,discount_percent,coupon_valuein promotion applicationsrole,tier,is_premium,planin profile or session datatax_rate,shipping_costin order calculationscredits,balance,quota_remainingin usage-tracked features
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:
- Quantity fields with a minimum of 1: try 0 and -1
- Dropdowns with fixed options: try arbitrary string values
- Date fields with future-date restrictions: try past dates
- Read-only fields in the UI: they still appear in POST bodies — modify them
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:
- Rate limiting coverage — authentication, password reset, OTP verification, registration, and contact/messaging endpoints are probed automatically for missing or bypassable throttling
- Numeric field manipulation — request bodies containing numeric values are systematically tested with zero, negative, and boundary values to detect server-side validation gaps
- Sequential ID detection — responses containing numeric or date-based identifiers are flagged for enumeration risk, with context about whether access control compensates
- Header-based rate limit bypass — common bypass vectors (
X-Forwarded-For,X-Real-IP,CF-Connecting-IP) are tested against rate-limited endpoints to detect IP-based controls that can be spoofed - Workflow anomaly detection — where flow state can be inferred from request sequences, out-of-order replay is tested against terminal endpoints
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:
- Never trust the client with authoritative data. Prices, discounts, roles, and quotas should be computed server-side from data the server controls. If it appears in a request body, the server should be treating it as advisory at best and ignoring it at worst.
- Enforce workflow state server-side. Multi-step flows are only as secure as the server's enforcement of step ordering. If advancing to step N without completing step N-1 is possible via the API, the client-side guard is not a security control.
- Rate limiting is a design requirement, not an afterthought. Every endpoint that triggers authentication, messaging, or resource creation needs rate limiting defined before implementation, not added after the first abuse report.
- Use opaque identifiers by default. UUIDs over auto-increment integers for anything user-facing. Enumeration becomes the attacker's problem rather than yours.
- Model abuse cases explicitly. The best defense against business logic flaws is a development process that includes abuse case analysis alongside functional requirements — for every feature, "how would an adversary misuse this?" should be a required question, not an optional exercise.
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