GraphNode
Back to OWASP Top 10 hub
OWASP A04

OWASP A04:2021 Insecure Design: Threat Modeling and Secure Design Patterns

| 11 min read |GraphNode Research

TL;DR

Insecure Design is a new entry in the OWASP Top 10 (2021), ranked at A04:2021. It is fundamentally different from the other categories: it covers risks that originate before any code is written — flaws in architecture, business-logic design, and threat models that no scanner can fully catch. SAST, DAST, and SCA find implementation defects; insecure design lives upstream of all of them. The discipline that addresses A04 is threat modeling (STRIDE, PASTA, attack trees), combined with secure design patterns, paved-road frameworks, and security requirements baked into the product backlog. Tooling helps surface implementation symptoms — missing encryption, hardcoded reset tokens, absent rate-limit checks — but does not replace design-phase work.

Insecure Design joined the OWASP Top 10 in 2021 to cover a category that scanner-based tooling alone cannot solve. Until the 2021 edition, the Top 10 was implicitly an implementation-defect taxonomy — broken access control, injection, cryptographic failures, all of which describe code that does the wrong thing. OWASP rebalanced the methodology in 2021 to weight incidence rate more heavily, and the rebalance made room for a class of risk the old framing had been ignoring: applications that are perfectly implemented against a fundamentally weak design.

The category matters because it forces the conversation upstream. A password-reset flow that exposes whether an email is registered is not a coding bug; the developer wrote exactly what was specified. A booking endpoint with no rate limit on inventory holds is not a typo; nobody asked for the limit. A login flow that issues long-lived JWTs with no revocation path is doing precisely what the architect drew on the whiteboard. These are design-time flaws, and the fix is not a patch — it is a redesign. This guide walks through what insecure design covers, the patterns it shows up as, the publicly verifiable cases it explains, the CWEs it maps to, and the layered process that actually addresses it.

What Insecure Design Means

The simplest framing is the distinction between design-time and implementation-time flaws. An implementation flaw is when the code does not match the design — a SQL query built by string concatenation when the design called for parameterization, a missing authorization check that the spec assumed would be there, an encryption call accidentally omitted. An insecure-design flaw is when the code matches the design exactly and the design itself is wrong. The developer can be reviewed, the linter can be tightened, the SAST engine can be tuned to perfection, and the application is still insecure because the underlying plan never accounted for the threat.

A useful test: if you handed the design document to a competent attacker before any code was written, would they already know how to abuse it? If yes, the design is insecure regardless of what the implementation eventually looks like. A booking flow that allows unlimited reservations without payment is abusable on the whiteboard. A password reset that emails a permanent link is abusable on the whiteboard. A multi-step transaction that does not verify state continuity is abusable on the whiteboard. These are pre-code defects.

The honest implication is that tooling cannot substitute for design. A scanner reads the code that exists; it cannot read the threat model that should have existed. A scanner can tell you that an encryption call is missing; it cannot tell you that the design forgot to require encryption in the first place. A scanner can flag a missing rate-limit middleware; it cannot tell you the architect never specified one. The OWASP Top 10 added A04 in 2021 precisely to make this point explicit and to push the security conversation upstream into the planning and architecture phases where it actually belongs.

Common Patterns

Missing rate limiting on sensitive endpoints. The design specifies a login endpoint, a password reset, an OTP verification, a phone-number lookup — and never specifies what happens when an attacker hits the endpoint a million times. The implementation honors the design and ships without a limit. Credential stuffing, user enumeration, brute force, and inventory abuse all start here. Rate limiting is rarely a bug in code; it is almost always missing from the design.

Weak account recovery flows. A password reset that authenticates the user purely by ownership of an email account inherits whatever security that email account has — typically much less than the application itself requires. Reset links that never expire, reset codes that are short enough to brute-force in minutes, and recovery flows that bypass MFA are all design defects. The implementation does what the spec says; the spec is the problem.

Trust boundary confusion. The design treats client-side validation as the primary control — a price field validated only in JavaScript, a hidden form field used to carry authorization state, a JWT claim trusted indefinitely after issuance. The trust boundary between user-controlled data and server-controlled state is drawn in the wrong place. The code enforces what the design says; the design says trust the client.

Single point of failure for authentication. A design that places an entire authentication regime behind a single token, a single secret, or a single third-party identity provider with no fallback path. When the single point fails — token leak, secret rotation incident, IdP outage — there is no graceful degradation. Defense in depth is a design concern; if the architecture diagram has a single critical node, the design is insecure regardless of how the node is implemented.

No defense in depth. A request flow that has exactly one check between the user and the resource, with the assumption that one check is enough. Reality is messy: middleware gets removed in refactors, authorization annotations get forgotten on new endpoints, perimeter controls are bypassed by internal callers. A design that does not anticipate the failure of any one layer is insecure by construction.

Business-logic flaws. Refund automation that does not verify the original transaction, price negotiation flows where the discount is calculated on the client and trusted on the server, multi-step transactions that allow step-skipping or replay, coupon stacking that was never bounded, voucher generation with sequential identifiers. These are abuse cases that competent threat modeling would have caught and that no scanner can find, because the application is doing exactly what it was designed to do — the design itself enables the abuse.

Real-World Examples

Twitter phone-number enumeration (2022). A vulnerability disclosed in 2022 allowed an attacker to confirm whether a given phone number or email address was associated with a Twitter account by submitting it to an authentication-related endpoint and observing the response. Roughly 5.4 million accounts were enumerated by a single actor before the issue was patched. The defect was not a coding bug — the endpoint did exactly what it was designed to do. The defect was that the design never required rate limiting or response uniformity to prevent the enumeration in the first place. A textbook insecure-design pattern: the implementation faithfully reflects an architecture that did not anticipate the abuse case.

Banking and SaaS password-reset weaknesses. Public disclosures across the financial-services and SaaS sectors over the past decade repeatedly surface the same shape: a password-reset flow that leaks account existence through differential responses, a recovery flow that bypasses MFA, a reset link with no expiry, or a reset code short enough to brute-force when no rate limiting is present. In each case, the recovery flow is doing what the spec asked; the spec asked for the wrong thing. Equifax's post-breach disclosures and several large bank advisories from 2018 onward describe variants of this pattern.

ATM jackpotting. ATM jackpotting attacks — where attackers cause a machine to dispense its full cash inventory — succeed in part because of insecure authentication design. Many deployed ATMs accept commands from a presumed-trusted internal network without re-verifying that the originator is the legitimate dispense controller, and use authentication tokens that the design assumed would never reach an attacker. Once the assumption breaks (physical tampering, insider, or supply-chain compromise of the controller), the implementation honors the forged commands. The defect is in the trust model the design committed to, not in the firmware that executes it.

Relevant CWE Mappings

A04:2021 aggregates roughly 40 underlying CWEs in the official OWASP mapping, which is one of the broadest of any Top 10 category — a reflection of how many different ways a design can be wrong. The six below are the ones most teams encounter when a design defect leaves a visible trace in code or configuration.

CWETitleWhere It Shows Up
CWE-256Plaintext Storage of a PasswordDesign that stores credentials without hashing or with reversible encryption
CWE-269Improper Privilege ManagementRoles, scopes, or privileges that the design grants too broadly
CWE-311Missing Encryption of Sensitive DataData flows the design left unencrypted at rest or in transit
CWE-434Unrestricted Upload of File with Dangerous TypeUpload endpoints whose design never bounded acceptable file types
CWE-602Client-Side Enforcement of Server-Side SecurityAuthorization, validation, or pricing controls placed only on the client
CWE-657Violation of Secure Design PrinciplesThe umbrella entry — least privilege, fail-safe defaults, defense in depth not applied

CWE-657 is the umbrella under which most A04 findings sit when no more specific CWE fits. CWE-602 is the most common code-visible symptom of insecure design — controls placed on the client and trusted on the server. CWE-256 and CWE-311 surface in design reviews where data classification was incomplete. The breadth of the CWE list mapped to A04 is itself a hint about the category: design can fail in many directions, and the taxonomy reflects that.

Why Tooling Alone Cannot Solve A04

SAST, DAST, and SCA are all implementation-defect detectors. SAST reads source code and flags patterns that look wrong. DAST sends requests to a running application and observes whether it misbehaves. SCA reads dependency manifests and matches them against vulnerability databases. None of them read the design document, the threat model, or the architect's whiteboard. None of them can tell you that a missing rate limit was missing from the spec, that a password-reset flow was never red-teamed, or that the trust boundary was drawn in the wrong place.

The discipline that addresses A04 is threat modeling, performed by humans during planning and design. STRIDE (Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege) is the most widely adopted framework for systematically walking a proposed design and asking what an attacker could do at each component and trust boundary. PASTA (Process for Attack Simulation and Threat Analysis) is a heavier seven-stage process aimed at risk-driven, business-aligned threat modeling. Attack trees formalize the goal-oriented attacker view, working from "compromise the system" downward through the steps required to get there.

Programmatic frameworks complement the per-feature techniques. OWASP SAMM (Software Assurance Maturity Model) and BSIMM (Building Security In Maturity Model) describe what mature secure-design programs look like across an organization: where security touches the SDLC, who owns design review, how security requirements get into the backlog, and how the program improves over time. SAMM and BSIMM are not threat modeling techniques themselves — they are the management frame that makes per-feature threat modeling actually happen on a schedule. The combination is the practice that addresses A04: per-feature threat modeling with STRIDE or PASTA, embedded in an organization-wide program described by SAMM or BSIMM.

How SAST Helps Anyway

Even though SAST cannot replace threat modeling, it is far from useless against A04. Insecure designs leave implementation symptoms in code, and a static analyzer that reads the code can flag the symptoms even when the upstream defect is invisible to it. Missing input validation on an endpoint that the design never specified validation for. Hardcoded password-reset tokens or default secrets that the design assumed would be rotated but never were. Encryption calls absent from a data-handling path that the threat model would have required to be encrypted. Unsafe defaults left in framework configuration where the design never explicitly chose secure ones. Authorization checks missing from new endpoints because the design never named them as protected.

GraphNode SAST traces user-controlled data across method boundaries with interprocedural data flow analysis, and runs 780+ rules covering OWASP Top 10 categories including the implementation symptoms of design flaws — hardcoded secrets, missing or weak cryptography, deserialization patterns, and similar code-visible signals. When threat modeling has identified a class of risk to control, SAST is the layer that confirms whether the chosen controls actually appear in code; when threat modeling has been skipped, SAST is the layer that surfaces enough symptoms to make the absence of design noticeable. The honest framing is that GraphNode SAST is a complement to threat modeling, not a substitute for it.

Prevention

Embed security in the design phase. The cheapest place to fix a design flaw is on the whiteboard, before any code is written. Establish a lightweight security review for new features and major architectural changes. The bar is not heavyweight documentation; it is a structured conversation about what could go wrong, who could abuse the design, and which controls the design needs to commit to.

Threat-model per major feature. Every new feature with a non-trivial trust boundary deserves a focused threat-modeling session. STRIDE works well as a checklist for short sessions; attack trees and PASTA fit longer ones. Output the session into abuse-case requirements that go on the backlog alongside use cases — a feature is not done until both have been satisfied.

Build a secure design patterns library. Most product teams reinvent the same flows: authentication, password reset, file upload, multi-tenant isolation, rate limiting, sensitive-data redaction. A reference library of vetted patterns, with the threat model already done, lets new teams adopt a known-good design rather than design from scratch. The library is a force multiplier — the more flows it covers, the less original design happens, and the fewer chances there are for a new design to be insecure.

Use paved-road frameworks. Spring Security, Django auth, Devise, ASP.NET Identity, Auth0 SDKs and similar opinionated libraries encode secure design choices as defaults. Teams that build on a paved road inherit the design work; teams that hand-roll authentication and authorization re-litigate every decision and reliably get some of them wrong. Choose paved roads as a matter of policy, not preference.

Secure-by-default infrastructure. Pulumi, Terraform, and Helm modules with hardened defaults — TLS on, minimal IAM scopes, deny-by-default network policies, encryption at rest enabled — propagate good design every time a service is provisioned. Centralized modules with security baked in turn the secure choice into the default choice.

Security champions in design reviews. A trained security champion embedded in each product team is the closest thing to a scalable threat-modeling practice. Champions attend design reviews, run STRIDE walkthroughs, and escalate genuine risks to the central security team. For the broader picture of how this fits into an AppSec program, see application security.

Frequently Asked Questions

What is insecure design?

Insecure design is the OWASP A04:2021 category covering risks that originate in the architecture, threat model, or business-logic design of an application — before any code is written. A perfectly implemented application can still be insecure if the design itself is weak: a password-reset flow that leaks account existence, a booking endpoint with no rate limiting, a multi-step transaction that allows step-skipping. The defect is upstream of implementation, and the fix is a redesign rather than a patch.

How is A04:2021 different from the other OWASP Top 10 categories?

Most other Top 10 categories describe implementation defects: code that does the wrong thing, configuration that was never hardened, a dependency that has a known CVE. Insecure Design describes a design that is wrong even when implemented faithfully. That difference matters because the detection layers differ: implementation flaws are caught by SAST, DAST, SCA, and configuration scanning; design flaws are caught by threat modeling and design review. Treating A04 as if it were detectable by the same scanner that catches A03 Injection is the most common mistake teams make with the category.

Can SAST detect insecure design?

SAST cannot detect insecure design directly, because the design itself is not in the source code. SAST can detect implementation symptoms of design flaws — missing input validation, hardcoded secrets, missing encryption calls, unsafe defaults, missing authorization annotations on new endpoints — and surfacing those symptoms is genuinely useful. But the upstream defect (the threat model that was never built, the abuse case that was never considered) is invisible to a static analyzer. The honest position is that SAST is a complement to threat modeling, not a substitute for it. A complete A04 program needs both.

What is threat modeling?

Threat modeling is a structured analysis of a proposed system that asks what an attacker could do at each component and trust boundary, and what controls the design needs to commit to in response. The most widely adopted framework is STRIDE (Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege), which works as a checklist for walking each component. Heavier alternatives include PASTA (a seven-stage risk-driven process) and attack trees (a goal-oriented attacker view). The output is a set of identified risks with chosen mitigations, ideally captured as abuse-case requirements in the backlog alongside the use cases.

Why was insecure design added in 2021?

OWASP rebalanced the 2021 Top 10 ranking methodology to weight incidence rate more heavily than exploitability and impact, which made room for risk classes that the older methodology had effectively excluded. Insecure design also reflects a maturing industry conversation: as scanners got better at finding implementation defects, the residual risk increasingly sat in classes of weakness that no scanner could touch — designs that were faithfully implemented but fundamentally wrong. Promoting insecure design to a top-level category was OWASP's way of forcing teams to recognize that design-time work is a separate, irreplaceable layer of an AppSec program.

Find Implementation Symptoms of Design Flaws in Source Code

GraphNode SAST surfaces hardcoded secrets, missing encryption, unsafe defaults, and missing authorization across 13+ languages with 780+ rules — the code-visible symptoms that signal an upstream design gap.

Request Demo