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.
The implementation is clean. The code passes all tests. Where does the design fail?
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.
The fix isn't a code patch. It's a redesign of the verification flow — changing the token space, attempt limits, and failure behaviour.
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.
Five beliefs developers hold about design security. Each one sounds reasonable. Click to see why it fails.
A team adds per-IP rate limiting (5 attempts per minute) to their 6-digit OTP endpoint. Is the brute force problem solved?
What is the key difference between "insecure design" and "insecure implementation"?
What practice does OWASP recommend as the primary defence against insecure design?
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 DesignFive things you can review right now — before writing another line of code.