OculusCyber Logo

OculusCyber

Home

Browse Topics


OWASP: Insecure Design

By Admin

November 9, 2025


Bad Example — Insecure Design

Scenario

A banking app lets users transfer money by trusting the "fromAccountId" in the request body.No one thought: "What if the attacker changes that field?"

// BAD: flawed business logic design
@WebServlet("/transfer")
public class MoneyTransferServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String fromAccount = req.getParameter("fromAccountId");
        String toAccount   = req.getParameter("toAccountId");
        double amount      = Double.parseDouble(req.getParameter("amount"));

        // ❌ Design flaw: assumes client input = truth
        // ❌ No check if the logged-in user actually owns fromAccount
        BankService.transfer(fromAccount, toAccount, amount);

        resp.getWriter().println("Transfer successful!");
    }
}

⚠️ What's wrong

  • Trusts user-supplied data for authorization context.
  • No ownership check for fromAccount.
  • An attacker can simply POST:
    fromAccountId=123456 (victim)
    toAccountId=999999 (attacker)
    amount=1000
    
  • Violates OWASP A04: Insecure Design, CWE-602 (Client-Side Enforcement of Server-Side Security), and CWE-285 (Improper Authorization).

This is not a "code bug" — it's a logic flaw caused by insecure assumptions in the design.

Good Example — Secure Design

Fix: Enforce authorization and business rules server-side

@WebServlet("/transfer")
public class SecureMoneyTransferServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        HttpSession session = req.getSession(false);
        if (session == null || session.getAttribute("userId") == null) {
            resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Login required");
            return;
        }

        String loggedInUserId = (String) session.getAttribute("userId");
        String fromAccount = req.getParameter("fromAccountId");
        String toAccount   = req.getParameter("toAccountId");
        double amount      = Double.parseDouble(req.getParameter("amount"));

        // ✅ Check ownership of the source account
        if (!BankService.userOwnsAccount(loggedInUserId, fromAccount)) {
            resp.sendError(HttpServletResponse.SC_FORBIDDEN, "You do not own this account");
            return;
        }

        // ✅ Enforce business constraints
        if (amount <= 0 || amount > BankService.getAccountBalance(fromAccount)) {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid transfer amount");
            return;
        }

        BankService.transfer(fromAccount, toAccount, amount);
        resp.getWriter().println("Transfer completed securely.");
    }
}

✅ Why it's secure

  • Server-side ownership check prevents horizontal privilege escalation.
  • Business rule validation (no negative, zero, or overdraft transfers).
  • Session-based identity ensures design separation of authentication and authorization.
  • Fail-secure responses — returns 403/400 properly instead of "silent success."

Design-Level Secure Practices

Principle

Secure Design Action

Threat modeling early

Use STRIDE or misuse cases before writing code.

Least privilege

Users can only act within their authorization context.

Secure defaults

Deny by default; explicitly grant access.

Defense in depth

AuthN + AuthZ + Input Validation + Logging.

Business rule enforcement

Validate workflows (not just input).

Separation of duties

Keep authorization and business logic independent.

TL;DR

Bad design: "If the front end sends fromAccountId, it must be valid."Good design: "The backend enforces ownership, authorization, and business rules — never trusts the client."