Vam tancar el mòdul 2 anunciant que en el mòdul 3 recorreríem el Top Ten categoria a categoria sobre el codi real de BazarNube, convertint el vocabulari en coneixement operatiu. Comencem per on comença la llista de 2021: A01 – Pèrdua de Control d'Accés (Broken Access Control). No és casualitat que ocupi el primer lloc: és la categoria més estesa, la que afecta més aplicacions i la que té més impacte directe sobre la confidencialitat i la integritat de les dades.
El control d'accés decideix què pot fer cada usuari autenticat. Quan falla, un client normal de BazarNube pot llegir les comandes d'un altre client, un usuari sense permisos pot accedir al tauler d'administració, o qualsevol pot modificar recursos que no li pertanyen. En aquest curs ho il·lustrarem amb un cas molt típic: un endpoint de l'API Node/Express de BazarNube que retorna comandes fent servir directament l'identificador que arriba a la URL, sense comprovar si aquella comanda és de l'usuari que pregunta.
Avís legal i ètic: totes les tècniques d'atac que es mostren són il·lustratives i amb dades fictícies. Practica només sobre sistemes propis o amb autorització explícita i per escrit. Provar el control d'accés sobre sistemes de tercers sense permís és delicte a la majoria de jurisdiccions.
Contingut
- Autenticació vs. autorització: no confondre A01 amb A07
- IDOR i referències directes insegures a objectes
- Escalada horitzontal i vertical de privilegis
- Forçat de rutes i control d'accés al client
- CORS mal configurat com a error de control d'accés
- Principis de prevenció: denegar per defecte, autoritzar al servidor
- Models d'autorització: RBAC i ABAC
- Errors comuns i consells
- Exercicis i solucions
- Autenticació vs. autorització
Són dos controls diferents i consecutius:
| Concepte | Pregunta que respon | Categoria OWASP |
|---|---|---|
| Autenticació | Qui ets? Ets qui dius que ets? | A07 (lliçó 03-09) |
| Autorització / Control d'accés | Un cop identificat, què pots fer? | A01 (aquesta lliçó) |
Un sistema pot autenticar perfectament (login sòlid, MFA, sessions ben gestionades) i tot i així tenir el control d'accés trencat: sap qui ets, però no comprova si pots accedir al recurs que demanes. A01 s'ocupa d'aquest segon control.
- IDOR i referències directes insegures a objectes
Un IDOR (Insecure Direct Object Reference) passa quan l'aplicació exposa una referència a un objecte intern (un id de base de dades, un nom de fitxer) i decideix l'accés només a partir d'aquell valor controlat per l'usuari, sense verificar la propietat del recurs.
Codi vulnerable a BazarNube
Lucía, la backend lead, va escriure aquest endpoint per consultar el detall d'una comanda:
// routes/orders.js (API Node/Express de BazarNube) — VULNERABLE
router.get('/api/comandes/:comandaId', requireLogin, async (req, res) => {
// requireLogin nomes comprova que hi ha sessio valida (autenticacio)
const comanda = await db.query(
'SELECT * FROM orders WHERE id = $1',
[req.params.comandaId]
);
if (comanda.rows.length === 0) return res.status(404).json({ error: 'No trobat' });
res.json(comanda.rows[0]); // retorna la comanda sigui de qui sigui
});El middleware requireLogin garanteix que hi ha una sessió iniciada, però ningú comprova que la comanda pertanyi a l'usuari d'aquella sessió. La consulta filtra per id, no per propietari.
Com s'explota (il·lustratiu)
La clienta Ana (usuària 501) veu la seva comanda a GET /api/comandes/1042. Com que els ids són numèrics i correlatius, prova a incrementar-los:
GET /api/comandes/1043 -> comanda d'un altre client (nom, adreca, import) GET /api/comandes/1044 -> una altra mes
Amb un simple bucle pot descarregar l'historial de comandes de tota la botiga: dades personals, adreces i línies de compra. És una fuga massiva sense necessitat de "hackejar" res; n'hi ha prou amb canviar un número.
Codi corregit
La regla és: el servidor ha de lligar cada accés a la identitat de la sessió, no a l'identificador que envia el client.
// routes/orders.js — SEGUR
router.get('/api/comandes/:comandaId', requireLogin, async (req, res) => {
const comanda = await db.query(
// filtrem TAMBE pel propietari pres de la sessio del servidor
'SELECT * FROM orders WHERE id = $1 AND user_id = $2',
[req.params.comandaId, req.session.userId]
);
// Retornem 404 (no 403) per no revelar que l'id existeix pero es d'un altre
if (comanda.rows.length === 0) return res.status(404).json({ error: 'No trobat' });
res.json(comanda.rows[0]);
});Detalls clau:
req.session.userIdprové del servidor (de la sessió), mai d'un paràmetre que el client pugui manipular.- La condició
AND user_id = $2converteix la comprovació d'autorització en part de la mateixa consulta: si la comanda no és teva, simplement no existeix per a tu. - Respondre
404en lloc de403evita filtrar l'existència de recursos aliens (un petit reforç davant l'enumeració).
Com a reforç addicional, molts equips substitueixen els ids seqüencials per UUID o identificadors no endevinables. Compte: això dificulta l'enumeració, però no substitueix la comprovació de propietat. Un UUID filtrat (en un log, en un email) continuaria essent accessible. La defensa real és la comprovació al servidor.
- Escalada horitzontal i vertical de privilegis
- Escalada horitzontal: accedeixes a recursos d'un altre usuari del mateix nivell. L'IDOR de dalt és exactament això: l'Ana llegint comandes d'altres clients.
- Escalada vertical: obtens privilegis d'un rol superior. Per exemple, un client que invoca un endpoint d'administració.
Exemple vertical a BazarNube
// routes/admin.js — VULNERABLE: nomes comprova login, no el rol
router.post('/api/admin/products/:id/price', requireLogin, async (req, res) => {
await db.query('UPDATE products SET price = $1 WHERE id = $2',
[req.body.price, req.params.id]);
res.json({ ok: true });
});Qualsevol client autenticat podria canviar preus. La correcció exigeix comprovar el rol al servidor:
// middleware/authorize.js
function requireRole(...roles) {
return (req, res, next) => {
if (!roles.includes(req.session.role)) {
return res.status(403).json({ error: 'Prohibit' });
}
next();
};
}
// routes/admin.js — SEGUR
router.post('/api/admin/products/:id/price',
requireLogin, requireRole('admin'), async (req, res) => { /* ... */ });El rol es pren de req.session.role, establert al login a partir de la base de dades, mai d'un camp enviat pel client (com un role al cos o en una cookie manipulable).
- Forçat de rutes i control d'accés al client
Un error freqüent en SPAs com el front React de BazarNube és amagar botons o rutes d'administració al client i creure que això protegeix. No protegeix: el navegador és territori de l'atacant.
// AdminLink.jsx — aixo es UX, NO seguretat
{user.role === 'admin' && <Link to="/admin">Tauler</Link>}Amagar l'enllaç millora l'experiència, però qualsevol pot navegar directament a /admin o, més important, cridar POST /api/admin/... amb curl. Tota decisió d'autorització s'ha de repetir i fer complir al servidor. El client pot reflectir permisos; el servidor els ha d'imposar.
El forçat de rutes (forced browsing) consisteix precisament a demanar directament URLs no enllaçades (/api/admin, /backup.zip, /api/internal/metrics) confiant que estiguin desprotegides. Es preveu aplicant el mateix control d'accés a tots els endpoints, inclosos els que no apareixen a la interfície.
- CORS mal configurat
CORS (Cross-Origin Resource Sharing) controla quins orígens web poden llegir respostes de la teva API des del navegador. Una configuració laxa pot convertir-se en un error de control d'accés.
// VULNERABLE: reflecteix qualsevol origen i a mes permet credencials
app.use(cors({
origin: (o, cb) => cb(null, true), // accepta TOTS els origens
credentials: true // ...enviant cookies de sessio
}));Reflectir l'Origin de l'atacant juntament amb credentials: true permet que un lloc maliciós faci peticions autenticades a l'API de BazarNube en nom de la víctima i llegeixi les respostes. La correcció és una llista blanca explícita:
// SEGUR
const allowed = ['https://bazarnube.com', 'https://app.bazarnube.com'];
app.use(cors({
origin: (o, cb) => cb(null, !o || allowed.includes(o)),
credentials: true
}));Mai facis servir origin: '*' combinat amb credencials (el mateix navegador ho bloqueja, però reflectir l'origen és l'equivalent perillós).
- Principis de prevenció
flowchart TD
A[Peticio entrant] --> B{Sessio valida?}
B -- No --> X[401 No autenticat]
B -- Si --> C{Autoritzat per aquest recurs?}
C -- No --> Y[403/404 Denegat per defecte]
C -- Si --> D[Executar accio]
Les regles d'or d'A01:
- Denegar per defecte. Tot endpoint parteix de "prohibit" i només es permet allò explícitament autoritzat. Res queda obert per oblit.
- Autoritzar sempre al servidor, amb identitat presa de la sessió, no de paràmetres del client.
- Centralitzar la lògica d'autorització en middlewares/serveis reutilitzables, no dispersa i copiada endpoint a endpoint (on és fàcil oblidar-la).
- Comprovar la propietat del recurs en cada accés a objectes (evita IDOR).
- Registrar els errors de control d'accés i alertar davant patrons d'abús (enllaça amb A09, lliçó 03-11).
- Models d'autorització: RBAC i ABAC
| Model | Idea | Exemple a BazarNube | Quan fer-lo servir |
|---|---|---|---|
| RBAC (basat en rols) | El permís depèn del rol de l'usuari | admin, suport, client |
Regles estables i per rol |
| ABAC (basat en atributs) | El permís depèn d'atributs d'usuari, recurs i context | "un venedor només edita productes de la seva botiga" | Regles fines, dependents de la dada |
BazarNube combina tots dos: RBAC per separar client/suport/admin, i una capa ABAC per a "cada venedor gestiona només el seu". L'IDOR de la secció 2 és, en el fons, una regla ABAC (comanda.user_id == sessio.userId) que faltava.
Errors Comuns i Consells
- Confiar en el client. Amagar botons no és autoritzar. Repeteix la comprovació al servidor, sempre.
- Fer servir l'id del client com a font d'identitat. Pren el
userId/rolede la sessió del servidor, mai del cos, la query o una cookie no signada. - Autorització copiada i enganxada. Si cada endpoint reimplementa la comprovació, algun se n'oblidarà. Centralitza en middlewares.
- Suposar que un UUID protegeix. Dificulta enumerar, no autoritza. Mantén la comprovació de propietat.
- Oblidar els mètodes "secundaris". Protegeixes
GETperò deixesPUT/DELETEoberts. Cobreix tots els verbs i tots els endpoints, inclosos els interns. - Consell: escriu proves automàtiques d'autorització ("l'usuari A no pot llegir la comanda de B") i executa-les a CI. Els IDOR es detecten molt bé amb tests.
Exercicis
Exercici 1. El següent endpoint de BazarNube permet descarregar una factura. Identifica la vulnerabilitat i corregeix-la.
router.get('/api/invoices/:id/pdf', requireLogin, async (req, res) => {
const inv = await db.query('SELECT pdf_path FROM invoices WHERE id = $1',
[req.params.id]);
res.download(inv.rows[0].pdf_path);
});Exercici 2. Marc proposa "arreglar" l'IDOR de comandes canviant els ids numèrics per UUID i no tocar la consulta. És suficient? Justifica-ho.
Exercici 3. Classifica cada escenari com a escalada horitzontal o vertical: a) Un client accedeix al carret d'un altre client. b) Un usuari de suport executa una acció reservada a administració. c) Un venedor edita un producte d'una altra botiga.
Solucions
Solució 1. És un IDOR: es descarrega qualsevol factura per id sense comprovar la propietat, i a més no valida que la fila existeixi (fallaria amb undefined). Correcció:
router.get('/api/invoices/:id/pdf', requireLogin, async (req, res) => {
const inv = await db.query(
'SELECT pdf_path FROM invoices WHERE id = $1 AND user_id = $2',
[req.params.id, req.session.userId]);
if (inv.rows.length === 0) return res.status(404).json({ error: 'No trobat' });
res.download(inv.rows[0].pdf_path);
});Solució 2. No és suficient. L'UUID només dificulta endevinar ids, però si un es filtra (email, log, historial del navegador compartit) l'accés continua obert perquè no hi ha comprovació de propietat. La defensa correcta és afegir AND user_id = $2. L'UUID és una capa complementària, no la principal.
Solució 3. a) horitzontal; b) vertical; c) horitzontal (mateix nivell de rol, diferent propietari del recurs; és una regla ABAC).
Conclusió
La pèrdua de control d'accés encapçala el Top Ten perquè és fàcil d'introduir (n'hi ha prou amb oblidar una comprovació) i molt danyina (fuga o manipulació de dades alienes). Les claus que anotem al backlog de BazarNube: denegar per defecte, prendre la identitat de la sessió del servidor, comprovar propietat i rol en cada accés, centralitzar l'autorització i no confiar mai en el client.
Entrada de backlog — A01: corregit l'IDOR a GET /api/comandes/:comandaId (afegit filtre per user_id), afegit requireRole('admin') als endpoints d'administració i substituït el CORS reflectit per llista blanca. Pendent: tests d'autorització a CI.
Un cop sabem que només la persona adequada accedeix a una dada, sorgeix la pregunta següent: quan aquella dada viatja o es desa, està protegida? Això ens porta a la lliçó següent, A02:2021 – Errors Criptogràfics i Exposició de Dades Sensibles, on veurem com BazarNube protegeix contrasenyes, targetes i trànsit.
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
