A06/Insecure Design — OWASP Top 1000 / 12
A06
OWASP Top 10 · 2025
Insecure Design
The code passed every test. The architecture failed on day one. A secure implementation can't fix an insecure design.
// password reset flow — a design that ships every day

A development team builds a standard password reset: the user enters their email, receives a 6-digit numeric code by SMS, and has 15 minutes to enter it. The code is validated server-side. The implementation is clean — parameterised queries, proper session handling, HTTPS everywhere. The code passes all tests. The security team reviews the source and finds no vulnerabilities. Then an attacker resets any account in under three hours.

This pattern appears in multiple real CVEs — including SeedDMS (CVE-2022-44938) and TP-Link routers (CVE-2023-23040). The problem isn't in the code. It's in the design decisions made before any code was written.

// identify the design flaw

The implementation is clean. The code passes all tests. Where does the design fail?

📧 SMS is insecure
SMS can be intercepted via SIM swapping or SS7 attacks on the carrier network.
🔢 Too few combinations
6 digits = 1 million combinations. With no rate limit on attempts, brute force finishes in hours.
⏱️ 15 minutes is too long
A shorter expiry would close the brute force window before the attacker finishes.
🔒 Missing HTTPS
The verification endpoint should be encrypted to prevent code interception.
// find the design flaw in the api spec

This isn't application code — it's the API specification. The endpoint works correctly. One design decision makes the entire flow brute-forceable. Click the line.

POST /api/reset-verify — design spec
// the brute force math
step 1 of 3

Redesign the flow, not the code.

flawed design
secure design

The fix isn't a code patch. It's a redesign of the verification flow — changing the token space, attempt limits, and failure behaviour.

🎯 the one habit

For every flow you design, write the abuse case: what happens if someone runs this a million times? Out of order? As someone else? If you can’t answer in 30 seconds, the design isn’t ready for code.

Token redesign
Rate limiting
Flow redesign
reset-verify spec
click the dot
why this works
// myths vs reality — click to flip

Five beliefs developers hold about design security. Each one sounds reasonable. Click to see why it fails.

common belief
"We have rate limiting, so brute force isn't possible."
reality
Per-IP rate limiting doesn't stop distributed attacks. Limit attempts per token, not per source. 100 IPs × 5 req/min = 500 req/min total.
common belief
"The code passes all tests, so the design is secure."
reality
Tests verify code matches the design spec. If the spec is wrong, 100% test coverage means nothing. Tests can't catch missing threat models.
common belief
"We can fix security issues after launch if they come up."
reality
Design flaws require architectural changes — database schema, API contracts, flow redesigns. Patching a design flaw costs 10-100× more than getting it right upfront.
common belief
"Only security specialists need to think about threat modeling."
reality
The developer writing the reset flow IS the person who needs to ask "what if someone tries all million codes?" Threat modeling is a design skill, not a security specialty.
common belief
"AI makes brute force attacks smarter and harder to defend against."
reality
For numeric OTPs, AI adds nothing — 000000 to 999999 is just a loop. But AI-powered phishing and credential stuffing make attackers far more likely to reach your reset flow. Design must assume every endpoint is found.
↑ click any card to flip ↑
// knowledge check
1 / 3

A team adds per-IP rate limiting (5 attempts per minute) to their 6-digit OTP endpoint. Is the brute force problem solved?

A — Yes — 5 per minute per IP makes brute force take years, which is effectively impossible
B — No — an attacker using distributed IPs bypasses per-IP limits. Limit attempts per token instead
C — Yes — combined with CAPTCHA on every attempt, this blocks both bots and manual attacks
// knowledge check
2 / 3

What is the key difference between "insecure design" and "insecure implementation"?

A — Insecure design only affects front-end code, while insecure implementation affects back-end code
B — Insecure design is caused by junior developers, while insecure implementation is caused by architects
C — Insecure design is a missing or flawed control that no amount of correct code can fix after the fact
// knowledge check
3 / 3

What practice does OWASP recommend as the primary defence against insecure design?

A — Threat modeling during design phase — identify abuse cases before writing any code at all
B — Comprehensive penetration testing after deployment to find design flaws in production systems
C — Static code analysis tools that detect architectural weaknesses through pattern matching in source
// self-check complete

// the mental model

carry this into your next design review

A secure implementation cannot fix an insecure design. If the architecture doesn't account for abuse, no amount of clean code, testing, or review will save it. Security starts at the whiteboard, not the editor.

A06 · Insecure Design
Scope in 2025
Business logic flaws · missing abuse cases · trust boundary violations · privilege design
A06 dropped from #4 to #6 in 2025, reflecting industry improvements in threat modeling adoption. 39 CWEs mapped, 729K occurrences. Key CWEs: CWE-256 (unprotected credentials), CWE-269 (improper privilege management), CWE-501 (trust boundary violation), CWE-522 (insufficiently protected credentials). The category explicitly covers business logic flaws — not code bugs, but missing controls in the design itself. Key distinction: A06 = the design decision was wrong (the spec is flawed even if the code is clean). A07 = the authentication implementation was wrong. Ownership is shared: rate limiting and lockout are commonly enforced at the gateway/API edge, not just in the application handler — the design fix is app + infra.
First fix
Threat model every flow — ask "what if an attacker does this 1 million times?"
Before writing code, list every endpoint and ask: what happens if someone calls this repeatedly? What if they change the user ID? What if they skip step 2 and go to step 3? STRIDE methodology gives structure: Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege. You don't need to be a security expert — you need to think like an attacker for 30 minutes.
Real-world cost
LastPass 2022 → $35M+ crypto theft through 2025 from a design assumption
LastPass's design assumed encrypted vaults couldn't be cracked even if exfiltrated. Users with weak master passwords (set before the 12-character minimum) had vaults that could be brute-forced offline — over months and years. By late 2025, TRM Labs traced $35M+ in cryptocurrency stolen from decrypted vaults. The encryption was technically correct. The design assumption — that users would always choose strong master passwords — was not. Design is where assumptions become attack surface.

// check your designs today

Five things you can review right now — before writing another line of code.

Password reset / OTP flow
How many digits? How many attempts before lockout? Is the limit per-token or per-IP?
Business logic abuse cases
Can someone book 600 seats at a discount? Redeem a coupon 1,000 times? Skip step 2?
Trust boundaries
What does the server trust from the client? Are prices, roles, or quantities validated server-side?
Threat model for critical flows
Have you asked "what if an attacker does this 1 million times?" for your auth and payment flows?
Fail-secure behaviour
What happens when a service is down? Does the system fail open (allow everything) or fail closed?
0 / 5