Fins ara hem corregit errors d'implementació: una consulta concatenada, una comprovació d'autorització oblidada, un innerHTML perillós. Però hi ha una classe de vulnerabilitats que cap correcció de codi elimina, perquè el problema no és en com es va escriure el codi, sinó en què es va decidir construir. Aquesta és A04:2021 – Disseny Insegur (Insecure Design), una de les dues categories noves del Top Ten 2021 (juntament amb A10 SSRF).
La idea central: pots implementar un disseny insegur de manera impecable —codi net, sense SQLi, sense XSS— i tot i així ser vulnerable, perquè faltaven controls de seguretat en el propi disseny. A BazarNube ho veurem amb el flux de cupons de descompte i checkout: un disseny que no va contemplar els límits de negoci permet que un client combini descomptes fins a pagar zero euros. No hi ha cap bug de codi; l'error és que ningú va pensar en l'abús en dissenyar el flux.
Aquesta lliçó introdueix el modelatge d'amenaces com a eina per descobrir aquests errors aviat. El presentem aquí de manera breu; el seu desenvolupament complet és al mòdul 7 (lliçó 07-02).
Avís legal i ètic: els escenaris d'abús són il·lustratius i amb dades fictícies. Practica només sobre sistemes propis o amb autorització explícita.
Contingut
- Disseny insegur vs. implementació insegura
- El cas dels cupons de BazarNube
- Requisits de seguretat i casos d'abús
- Límits de confiança i patrons de disseny segur
- Introducció al modelatge d'amenaces
- Errors comuns, exercicis i solucions
- Disseny insegur vs. implementació insegura
| Aspecte | Implementació insegura | Disseny insegur |
|---|---|---|
| Origen | Es va implementar malament un control que existia | Faltava el control en el disseny |
| Exemple | Consulta SQL concatenada (A03) | No es va definir límit d'intents ni de negoci |
| Es detecta amb | Revisió de codi, SAST/DAST | Modelatge d'amenaces, revisió de requisits |
| Es corregeix amb | Apedaçar el codi | Redissenyar el flux / afegir controls |
Una pista per distingir-los: si arregles l'error canviant com està escrita una funció, era d'implementació; si necessites afegir un control o regla de negoci que no existia, era de disseny. Molts incidents reals combinen tots dos, però A04 ens obliga a mirar abans del codi: el flux, tal com va ser concebut, resisteix un usuari maliciós?
- El cas dels cupons de BazarNube
L'equip de producte va demanar cupons de descompte per captar clients. El Marc i la Lucía van implementar el flux tal com es va especificar, i el codi és correcte: valida el cupó, comprova que existeix, aplica el percentatge. L'endpoint de checkout:
// checkout.js — codi CORRECTE pero DISSENY insegur
router.post('/api/checkout', requireLogin, async (req, res) => {
const cart = await getCart(req.session.userId);
let total = cartTotal(cart);
for (const code of req.body.coupons) { // accepta una LLISTA de cupons
const c = await getCoupon(code);
if (c && c.active) total -= total * (c.percent / 100); // s'apliquen tots
}
const order = await createOrder(req.session.userId, cart, total);
res.json({ orderId: order.id, total });
});No hi ha SQLi, ni IDOR, ni XSS. El codi fa exactament el que es va dissenyar. El problema és el que no es va dissenyar:
- No es va limitar el nombre de cupons per comanda (el flux accepta una llista).
- No es va comprovar si un cupó és acumulable amb d'altres.
- No es va validar un descompte màxim ni un import mínim a pagar.
- No es va limitar el nombre d'usos per client.
Com s'explota (abús de lògica)
Un client descobreix tres cupons vàlids del 40 %, 35 % i 30 %. Els envia tots en el mateix checkout:
Aplicats en cascada, el total cau gairebé a zero. Pitjor: com que no hi ha límit d'usos, automatitza comandes i buida l'estoc a cost simbòlic. Cap eina d'escaneig de codi ho detecta, perquè no hi ha cap patró insegur: és una decisió de negoci que va faltar.
Correcció: afegir els controls de disseny
La solució no és "apedaçar una línia", és incorporar els requisits de seguretat de negoci que faltaven:
// checkout.js — DISSENY segur: regles de negoci explicites
router.post('/api/checkout', requireLogin, async (req, res) => {
const cart = await getCart(req.session.userId);
const subtotal = cartTotal(cart);
// Regla 1: un unic cupo per comanda (no acumulables per defecte)
const code = req.body.coupon;
let discount = 0;
if (code) {
const c = await getCoupon(code);
if (!c || !c.active) return res.status(400).json({ error: 'Cupo invalid' });
// Regla 2: limit d'usos per client
if (await couponUsedBy(code, req.session.userId) >= c.maxPerUser)
return res.status(400).json({ error: 'Cupo ja utilitzat' });
// Regla 3: import minim de compra
if (subtotal < c.minPurchase)
return res.status(400).json({ error: 'No arriba al minim' });
discount = subtotal * (c.percent / 100);
}
// Regla 4: descompte maxim global i import minim a pagar
discount = Math.min(discount, subtotal * 0.5);
const total = Math.max(subtotal - discount, 0.01);
const order = await createOrder(req.session.userId, cart, total, code);
res.json({ orderId: order.id, total });
});Fixa't que la correcció són regles de negoci de seguretat, no tècniques de codificació: un cupó per comanda, límit d'usos, mínim de compra, límit de descompte i import mínim a pagar. Aquestes regles havien de sorgir en la fase de disseny, no descobrir-se després de l'abús.
- Requisits de seguretat i casos d'abús
L'origen de l'error va ser treballar només amb casos d'ús ("el client aplica un cupó i paga menys") i oblidar els casos d'abús ("el client combina cupons per pagar zero"). Dissenyar amb seguretat implica escriure tots dos.
| Cas d'ús (usuari legítim) | Cas d'abús (atacant) | Requisit de seguretat derivat |
|---|---|---|
| Aplica un cupó de benvinguda | Combina diversos cupons | Màxim un cupó per comanda |
| Fa servir el seu cupó un cop | Reutilitza el cupó en bucle | Límit d'usos per client |
| Compra amb descompte | Força total = 0 | Import mínim a pagar i límit de descompte |
| Es registra per rebre cupó | Crea comptes massius per farmejar cupons | Verificació de correu / límit per identitat |
De cada cas d'abús neix un requisit de seguretat que ha de formar part de l'especificació, no ser un afegit posterior. Aquest és el cor d'A04: la seguretat com a requisit de disseny.
- Límits de confiança i patrons de disseny segur
Un límit de confiança (trust boundary) és tota frontera on les dades passen d'una zona menys fiable a una altra de més fiable (del navegador al servidor, del servidor al mòdul de pagaments). En cada límit cal validar i tornar a decidir. L'error de disseny freqüent és assumir que "si ve del pas anterior, és de fiar".
flowchart LR A[Client React] -->|limit de confianca| B[API Express] B -->|limit de confianca| C[Modul pagaments Java] C -->|limit de confianca| D[PSP extern]
Patrons de disseny segur que BazarNube adopta:
- Validació al servidor de les regles de negoci, no només al client (el client és suggeriment, el servidor és autoritat).
- Límits i quotes per disseny: rate limiting, màxims per operació, imports mínims.
- Estats i transicions explícites: una comanda no pot passar de "creada" a "enviada" sense passar per "pagada". Dissenyar la màquina d'estats evita salts abusius.
- Fail-safe / denegar per defecte (ho vam veure a A01): davant del dubte, denegar.
- Reutilitzar components segurs provats en lloc de reinventar (llibreries de pagament, d'autenticació).
- Introducció al modelatge d'amenaces
El modelatge d'amenaces (threat modeling) és la pràctica que hauria evitat l'error dels cupons: analitzar el disseny abans de construir, preguntant-se què pot sortir malament. De manera resumida, respon a quatre preguntes (marc de Shostack):
- Què estem construint? (diagrama del flux, actors, límits de confiança)
- Què pot sortir malament? (amenaces; un mètode comú és STRIDE)
- Què farem al respecte? (controls/mitigacions)
- Ho hem fet bé? (validació)
Aplicat als cupons, la pregunta "què pot sortir malament?" hauria revelat de seguida "un client combina cupons" i "els fa servir en bucle", generant els requisits que faltaven.
No desenvolupem aquí el mètode complet (STRIDE, diagrames de flux de dades, priorització): això correspon al mòdul 7, lliçó 07-02 (Modelatge d'Amenaces). De moment, queda't amb la idea operativa: incorpora una sessió de modelatge d'amenaces en dissenyar cada flux sensible (pagaments, cupons, registre, permisos), i converteix cada amenaça en un requisit de seguretat verificable.
Errors Comuns i Consells
- Confondre "sense bugs" amb "segur". Un flux pot no tenir ni un sol bug de codi i ser abusable per disseny.
- Dissenyar només casos d'ús feliços. Escriu també els casos d'abús; d'aquí surten els requisits.
- Validar regles de negoci només al front. El client és manipulable; l'autoritat és el servidor.
- Deixar la seguretat per a "després". Redissenyar un flux en producció és caríssim; modelar amenaces al principi és barat.
- No definir límits ni quotes. Tot flux econòmic o de recursos necessita límits explícits.
- Consell: per a cada nova funcionalitat sensible, dedica 30 minuts a preguntar "com n'abusaria jo, d'això?" abans d'implementar-la.
Exercicis
Exercici 1. BazarNube dissenya un sistema de "punts de fidelitat": el client guanya punts per compra i els bescanvia per saldo. Enumera tres casos d'abús i el seu requisit de seguretat derivat.
Exercici 2. Quin d'aquests errors és de disseny i quin d'implementació? Justifica-ho. a) L'endpoint de cupons concatena el codi en una consulta SQL. b) El sistema permet retornar un producte i quedar-se'l perquè no verifica que s'hagi rebut al magatzem.
Exercici 3. L'equip proposa "arreglar" l'abús de cupons validant al front que només s'enviï un cupó. Resol el problema? Per què?
Solucions
Solució 1. Exemples: (a) Abús: generar compres i devolucions per farmejar punts → Requisit: només acreditar punts després d'una comanda completada i no reemborsada. (b) Abús: bescanviar més punts dels que es tenen per condició de cursa → Requisit: descompte atòmic i transaccional del saldo de punts. (c) Abús: transferir punts entre comptes propis creats en massa → Requisit: límit per identitat verificada / no transferibles.
Solució 2. a) Implementació: el control (parametritzar) existeix com a pràctica; es va implementar malament una consulta. b) Disseny: falta una regla/estat en el flux de devolucions (verificar la recepció abans de reemborsar); no és un bug de codificació sinó un control absent.
Solució 3. No el resol. La validació al front és només UX: un atacant crida directament POST /api/checkout amb diversos cupons saltant-se el navegador. Els controls de negoci s'han d'imposar al servidor (com a la secció 2). El front pot guiar, però no protegir.
Conclusió
A04 amplia la mirada: la seguretat comença en el disseny, no en el codi. Els errors de disseny insegur no els caça un escàner; es prevenen escrivint casos d'abús, derivant requisits de seguretat, respectant els límits de confiança i aplicant modelatge d'amenaces aviat. A BazarNube, redissenyar el flux de cupons amb límits de negoci explícits va tancar un forat que cap correcció de línia hauria tapat.
Entrada de backlog — A04: redissenyat el checkout de cupons amb regles de negoci (un cupó per comanda, límit d'usos, mínim de compra, límit de descompte, import mínim a pagar); adoptada una sessió de modelatge d'amenaces per a fluxos econòmics; pendent d'aplicar el mètode complet a M7.
Del disseny passem ara a com es desplega i configura el que s'ha construït. Encara que el codi i el disseny siguin correctes, una configuració descurada obre la porta. La lliçó següent és A05:2021 – Configuració de Seguretat Incorrecta, amb la config d'Express i Docker de BazarNube.
Curs d'OWASP: Directrius i Estàndards per a la Seguretat en Aplicacions Web
Mòdul 1: Introducció a OWASP
Mòdul 2: Principals Projectes d'OWASP
- OWASP Top Ten
- OWASP ASVS (Application Security Verification Standard)
- OWASP SAMM (Software Assurance Maturity Model)
- OWASP ZAP (Zed Attack Proxy)
- Altres Projectes Clau: WSTG, Cheat Sheets i Dependency-Check
Mòdul 3: OWASP Top Ten 2021 en Profunditat
- A01:2021 – Pèrdua de Control d'Accés
- A02:2021 – Errors Criptogràfics i Exposició de Dades Sensibles
- A03:2021 – Injecció
- Cross-Site Scripting (XSS) en Profunditat
- A04:2021 – Disseny Insegur
- A05:2021 – Configuració de Seguretat Incorrecta
- Entitats Externes XML (XXE)
- A06:2021 – Components Vulnerables i Desactualitzats
- A07:2021 – Errors d'Identificació i Autenticació
- A08:2021 – Errors d'Integritat de Programari i Dades (Deserialització Insegura)
- A09:2021 – Errors de Registre i Monitorització
- A10:2021 – Server-Side Request Forgery (SSRF)
Mòdul 4: OWASP ASVS (Application Security Verification Standard)
Mòdul 5: OWASP SAMM (Software Assurance Maturity Model)
Mòdul 6: OWASP ZAP (Zed Attack Proxy)
- Introducció a ZAP
- Instal·lació i Configuració
- Escaneig de Vulnerabilitats
- Automatització de Proves de Seguretat
Mòdul 7: Bones Pràctiques i Recomanacions
- Cicle de Vida de Desenvolupament Segur (SDLC)
- Modelatge d'Amenaces (Threat Modeling)
- Integració de Seguretat en DevOps (DevSecOps)
- Formació i Conscienciació en Seguretat
- Eines i Recursos Addicionals
Mòdul 8: Exercicis Pràctics i Casos d'Estudi
- Exercici 1: Identificació de Vulnerabilitats
- Exercici 2: Implementació de Controls de Seguretat
- Cas d'Estudi 1: Anàlisi d'un Incident de Seguretat
- Cas d'Estudi 2: Millora de la Seguretat en una Aplicació Web
