Insecure Design happens when security controls that should have been designed in from the start — like rate limiting and account lockout — are simply missing, no amount of clean code can patch a flawed blueprint. Try the interactive playground below to see how a password-reset flow with no rate limiting falls to automated guessing in seconds.
OWASP Top 10 · 2025 · A06 — Insecure Design
Insecure Design: When the Blueprint Itself Is the Vulnerability
Insecure design isn’t a coding bug you can patch — it’s a missing or inadequate security control baked
into how a feature was planned, before a single line of code was written. Think: a password-reset flow
that never considered limiting guesses, business logic that trusts whatever the client sends, or a checkout
process with no thought given to abuse cases. The code can be written “correctly” and still be dangerously
insecure, because the design never accounted for an attacker’s perspective.
Interactive Demo — Password Reset Code
Scenario: account recovery requires a 4-digit reset code sent to the user.
Question: what happens when someone just tries every combination?
Rate Limiting & LockoutOFF
Attacker / Tester Console
Account active — reset code required
Attempts made0
System ready. Awaiting reset-code attempts…
What’s actually happening (backend view)
This is what a real backend engineer would see in logs — visible here for teaching purposes only.
In production, none of this would be shown to the client.
Valid reset code: ???? (hidden until revealed)
Total possible 4-digit codes: 10,000
Attempt limit: none configured
Lockout policy: none configured
Design Comparison
⚠ Vulnerable design
Reset credential is a 4-digit numeric code — only 10,000 possible combinations.
No limit on the number of attempts a single user or IP can make.
No lockout, no delay, no CAPTCHA, no alerting on repeated failures.
Design assumption: “no one will bother guessing.” Guessable by script in seconds.
Client-side only feels secure — server never enforces any abuse limits.
✓ Secure design
Reset credential is a long random token (e.g. 32+ chars) sent via a trusted channel.
Rate limiting per account, per IP, and per session on every reset attempt.
Exponential backoff + account lockout after a small number of failures (e.g. 5).
Alerting / logging triggered on repeated-failure patterns for security review.
Threat-modeled at design time: “assume this endpoint will be attacked.”
Why This Matters
Insecure design flaws can’t be fixed with a quick patch — the feature has to be rethought from the
ground up, because the missing control (like rate limiting) was never part of the plan. Unlike a bug
in otherwise-sound logic, this is the logic working exactly as designed — the design itself just never
considered how an attacker would abuse it.
How to Detect & Fix
Threat-model during design, not after launch — ask “how would this be abused?” for every sensitive feature before it’s built.
Avoid short numeric codes for anything security-sensitive; use long, random, high-entropy tokens instead.
Enforce rate limiting, lockout, and exponential backoff server-side on login, reset, and other sensitive actions.
Apply secure-by-design principles (least privilege, defense in depth, fail securely) as requirements, not afterthoughts.
Test abuse cases, not just happy paths — include brute-force, race-condition, and logic-abuse scenarios in QA and pen testing.