$ cat owasp/a06.md
A06:2025
Insecure Design
The code does exactly what it was designed to do - and the design is the problem.
## 1 · why it happens
You cannot fix these with a library. A checkout that trusts the price sent by the browser, a coupon that can be applied twice if two requests arrive together, a password reset with no rate limit - each is a missing rule in the design.
The developer's best tool here is asking "what if?" while designing: what if the user sends this twice, out of order, with a negative number, a thousand times a second?
# JavaScript
app.post('/checkout', async (req, res) => {
// price comes from the client
await charge(req.user, req.body.items, req.body.total);
});app.post('/checkout', async (req, res) => {
const items = await loadItems(req.body.itemIds); // prices from the DB
const total = items.reduce((s, i) => s + i.price * i.qty, 0);
if (items.some((i) => i.qty < 1)) return res.sendStatus(400);
await charge(req.user, items, total);
});# C#
// Two parallel requests can both pass this check
var coupon = await _db.Coupons.FindAsync(code);
if (!coupon.Used)
{
ApplyDiscount(order, coupon);
coupon.Used = true;
await _db.SaveChangesAsync();
}// Atomic: only one request can flip Used from false to true
var updated = await _db.Coupons
.Where(c => c.Code == code && !c.Used)
.ExecuteUpdateAsync(s => s.SetProperty(c => c.Used, true));
if (updated == 1) ApplyDiscount(order, code);## 2 · how attackers find it from outside
| public source | what it tells them |
|---|---|
| Public docs & help pages | How your workflows work (refunds, coupons, invites) - the logic to abuse. |
| Mobile app / JS bundle | Client-side checks (price, limits) that the server may not repeat. |
→ all OSINT sources · run them on yourself
## 3 · how it's abused
⚠ Recon your own assets, or targets you have written permission to test. Passive OSINT reads public data, but using it to target someone else's systems can still be illegal or against their terms. Examples on this site use example.com placeholders.
{"itemId": 42, "qty": -1}Business logic: a negative quantity turns a purchase into a refund.
20 × POST /redeem-coupon in parallelRace condition: all requests check "not used yet" before any of them marks it used.
10,000 × POST /verify-otpNo rate limit: a 6-digit code can be brute-forced.
Payload examples adapted from PayloadsAllTheThings (MIT, © Swissky). Full lists:Business Logic Errors · Race Condition · Brute Force Rate Limit
## 4 · fix
- Threat-model new features: write down how they could be abused.
- Never trust client-side values for price, role or limits.
- Use atomic database operations or locks for one-time actions.
- Rate-limit every sensitive action (login, OTP, reset, coupon).
## 5 · detect
Official description: OWASP A06:2025 Insecure Design