A la lliçó anterior vas col·locar el mapa: els dominis de la disciplina, els marcs que l'organitzen i —sobretot— el cicle de vida d'un atac. Ara omplirem aquest esquelet de contingut. Aquesta lliçó recorre els atacs tècnics que s'executen contra sistemes com el de Nimbus Reservas, ordenats per la fase de la cadena en què apareixen: com l'adversari recull informació, com ataca la xarxa, com aconsegueix credencials, com explota una aplicació web, com desplega ransomware o compromet una cadena de subministrament i com abusa d'una API. De cadascun en veuràs què és, un exemple mínim i conceptual sobre l'arquitectura de Nimbus, com es detecta i cap on apunta la seva defensa. L'enfocament és defensiu en tot moment: els exemples són il·lustratius, mínims i sempre acompanyats de la seva correcció. Conèixer l'atac no és un fi, és el requisit per dissenyar la defensa.
Contingut
- Com s'organitza aquest catàleg i un advertiment previ
- Fase 1 — Reconeixement: OSINT i escaneig
- Atacs a la xarxa: escolta, suplantació i interposició
- Denegació de servei: volumètrica i aplicativa
- Atacs a credencials i sessions
- Atacs a aplicacions web: el Top 10 d'OWASP com a marc
- Programari maliciós en acció: ransomware modern i doble extorsió
- Atacs a la cadena de subministrament
- Atacs específics a API
- Un atac encadenat sobre Nimbus, pas a pas
- Taula resum: atac, propietat afectada, detecció i defensa
- Com s'organitza aquest catàleg i un advertiment previ
Un catàleg d'atacs sense estructura és una llista impossible de recordar. Per això fem servir l'eix que ja coneixes —les fases de la Kill Chain— i hi afegim una segona clau de lectura: quina propietat de la tríada CIA trenca cada atac, la que vas establir a 01-01.
flowchart LR
R["RECONEIXEMENT\nOSINT, escaneig,\nenumeracio"] --> E["LLIURAMENT I EXPLOTACIO\nXarxa, credencials,\naplicacio web"]
E --> P["POST-EXPLOTACIO\nPersistencia, moviment\nlateral, escalada"]
P --> A["ACCIONS\nExfiltracio, xifratge,\nfrau, denegacio"]
Advertiment necessari. Tot el que segueix està escrit per defensar. Aquí no hi trobaràs eines llestes per atacar, codi funcional de programari maliciós ni instruccions aplicables a sistemes de tercers. Els exemples són fragments mínims sobre l'entorn fictici de Nimbus, sempre amb la seva correcció al costat. Executar qualsevol d'aquestes tècniques contra sistemes que no són teus i sense autorització expressa i per escrit és un delicte; el marc legal i ètic de les proves autoritzades es tracta a 06-06, i la metodologia de les proves de penetració a 05-03.
- Fase 1 — Reconeixement: OSINT i escaneig
El reconeixement és la fase que les organitzacions ignoren perquè no deixa rastre als seus sistemes: bona part passa sense tocar-los.
2.1 OSINT: intel·ligència de fonts obertes
Què és. Recopilar informació públicament disponible sobre l'objectiu. No s'explota res; es llegeix el que l'organització ha publicat sense adonar-se'n.
Què troba un atacant sobre Nimbus sense tocar ni un sol servidor:
| Font pública | Què revela | Ús per a l'atacant |
|---|---|---|
| Registres de transparència de certificats | Tots els subdominis per als quals s'ha emès un certificat, inclosos pre., admin. i demo. |
Descobreix entorns interns que ningú no pensava que fossin públics |
| Perfils professionals de l'equip | Noms, càrrecs, tecnologies que esmenten («migrant a FastAPI i PostgreSQL») | Objectius de phishing i pila tecnològica |
| Repositoris de codi públics | Fitxers de configuració pujats per error, correus en els commits, noms d'amfitrió interns | Credencials i superfície interna |
| Ofertes de feina | «Busquem DevOps amb experiència en X, Y, Z» | Inventari tecnològic gratis i molt fiable |
| Registres DNS i WHOIS | Proveïdors de correu, de núvol, de CDN | Per on entra el correu i on està allotjat |
| Filtracions de credencials d'altres serveis | Correus corporatius apareguts en bretxes de tercers | Base per a credential stuffing (apartat 5) |
| Documents publicats (PDF, fulls de càlcul) | Metadades amb noms d'usuari, rutes internes, versions de programari | Convencions de noms de compte |
Com es defensa. No es pot impedir que algú llegeixi el que és públic, però sí reduir el que es publica sense voler:
- Revisar periòdicament els subdominis que apareixen en transparència de certificats i retirar els que ja no existeixen (és la troballa de l'exercici 3 de la lliçó anterior).
- Escanejar l'històric dels repositoris buscant secrets —no n'hi ha prou d'esborrar el fitxer: a Git continua estant a l'historial— i rotar qualsevol credencial exposada.
- Netejar metadades dels documents publicats.
- Assumir que la llista de correus de l'equip és pública. La defensa no és amagar-la, és MFA i formació (02-03, 02-05).
2.2 Escaneig i enumeració
Què és. Contactar activament amb els sistemes de l'objectiu per descobrir què hi ha: quines IP responen, quins ports estan oberts, quins serveis i versions corren, quines rutes existeixen a l'aplicació web.
A diferència de l'OSINT, l'escaneig sí que deixa rastre. I aquí hi ha l'asimetria important per a una pime: Nimbus ja està sent escanejada contínuament per bots que no saben què és. Distingir l'escaneig dirigit del soroll de fons és difícil; el que sí que és factible és fixar-se en escanejos lents, ordenats i des d'una mateixa font, que suggereixen interès real.
# Extracte del registre del balancejador de Nimbus (fragment illustratiu)
203.0.113.44 - - [12/Mar/2026:03:11:02] "GET /.env HTTP/1.1" 404 153
203.0.113.44 - - [12/Mar/2026:03:11:03] "GET /.git/config HTTP/1.1" 404 153
203.0.113.44 - - [12/Mar/2026:03:11:04] "GET /admin HTTP/1.1" 404 153
203.0.113.44 - - [12/Mar/2026:03:11:05] "GET /backup.sql HTTP/1.1" 404 153
203.0.113.44 - - [12/Mar/2026:03:11:06] "GET /wp-login.php HTTP/1.1" 404 153
203.0.113.44 - - [12/Mar/2026:03:11:07] "GET /phpmyadmin/ HTTP/1.1" 404 153Com es llegeix aquest extracte:
- Sis peticions en sis segons des de la mateixa IP, totes a rutes que no existeixen a Nimbus. És un escàner automatitzat provant el catàleg habitual.
/wp-login.phpi/phpmyadmin/delaten que el bot no sap quina tecnologia fa servir Nimbus: ho prova tot. Això és l'atac oportunista de 01-04 en estat pur.- El senyal defensiu més valuós no és als 404, sinó el dia que un d'ells retorni 200. Per això l'alerta útil no és «tenim escanejos» (els tens sempre), sinó «una ruta sensible ha retornat alguna cosa diferent de 404».
Les eines de descobriment i escaneig es veuen a 05-01 i 05-03. Aquí interessa el concepte i la seva contrapartida defensiva: cada servei que no exposes és un servei que no apareix en cap escaneig.
- Atacs a la xarxa: escolta, suplantació i interposició
Aquests atacs ataquen el canal, no els extrems. Comparteixen una premissa: si l'atacant aconsegueix estar al mig o escoltar, la seguretat dels extrems deixa de bastar.
| Atac | Què fa | Requisit | Propietat trencada |
|---|---|---|---|
| Sniffing (escolta) | Captura trànsit que circula per la xarxa | Ser al mateix segment o en un punt de pas | Confidencialitat |
| ARP spoofing | Enverina la taula ARP de la xarxa local perquè el trànsit passi per l'atacant | Ser a la mateixa xarxa local (la wifi de l'oficina) | Confidencialitat, integritat |
| Man-in-the-Middle (MitM) | S'interposa i pot llegir i modificar el trànsit | Algun dels anteriors, o control d'un punt de xarxa | Confidencialitat, integritat, autenticitat |
| DNS spoofing / enverinament | Respon a consultes DNS amb una IP falsa | Control del resolutor, de la xarxa o del compte de DNS | Autenticitat, integritat |
| Punt d'accés wifi fals | Ofereix una xarxa amb nom creïble («NimbusGuest») a la qual la víctima es connecta | Proximitat física | Totes |
3.1 Per què això continua important si tot va per HTTPS
És la pregunta correcta. TLS ben implementat fa que l'escolta passiva sigui inútil: l'atacant veu trànsit xifrat. Però queden tres forats reals a Nimbus:
- El trànsit intern que no va xifrat. Al DFD de 01-04, el flux F3 (balancejador → API) era HTTP intern. Algú que arribi a aquesta xarxa el llegeix en clar. És el motiu del principi Zero Trust: la xarxa interna no és de confiança.
- Les metadades continuen visibles. Encara que el contingut estigui xifrat, un observador veu a quins dominis et connectes, quan i quin volum. La consulta DNS prèvia sol anar en clar.
- La degradació i l'error d'usuari. Si un portàtil de Nimbus accepta un certificat no vàlid amb un clic, TLS deixa de protegir. Per això existeix HSTS (que veurem a 02-04) i per això els clients han de rebutjar, no preguntar.
Exemple mínim de l'error clàssic al codi de Nimbus:
# === VULNERABLE: desactivar la verificacio de certificat ===
import requests
# Algu va posar verify=False "perque en proves donava error de certificat"
resposta = requests.get("https://api.pasarela-pago.example/v1/cobros",
headers=capceleres, verify=False)# === CORRECTE ===
import requests
# La verificacio del certificat es el que converteix TLS en autenticacio
# del servidor, no nomes en xifratge. Sense ella, qualsevol que s interposi
# pot presentar el seu propi certificat i llegir i modificar la comunicacio.
resposta = requests.get("https://api.pasarela-pago.example/v1/cobros",
headers=capceleres, timeout=10) # verify=True es el valor per defectePer què verify=False és tan greu i tan freqüent: desactiva la comprovació que el certificat presentat correspon realment al domini i està signat per una autoritat reconeguda. El trànsit continua xifrat, cosa que dona una falsa sensació de seguretat, però xifrat contra l'atacant si aquest s'hi ha interposat. Apareix gairebé sempre com a pedaç temporal en un entorn de proves i acaba a producció. Regla pràctica: verify=False no ha d'existir en cap repositori; si un certificat intern dona problemes, la solució és afegir l'autoritat interna al magatzem de confiança, no desactivar la verificació. Una anàlisi estàtica al CI (02-04) ho detecta automàticament.
3.2 Detecció
| Senyal | On es veu |
|---|---|
| Canvis inesperats a la taula ARP; una MAC associada a diverses IP | Registres del commutador; eines de detecció d'ARP a la xarxa local |
| Respostes DNS amb TTL anòmals o IP fora dels rangs esperats | Registres del resolutor |
| Certificat presentat diferent de l'esperat | Certificate pinning a l'app mòbil; alertes de transparència de certificats |
| Aparició d'un SSID amb el nom corporatiu fora de l'oficina | Inventari de punts d'accés; alertes de punts d'accés no autoritzats |
- Denegació de servei: volumètrica i aplicativa
L'objectiu és la disponibilitat: impedir que els clients de Nimbus facin servir el servei. Per a un SaaS del qual depenen agendes de clíniques en marxa, una hora de caiguda té un cost immediat i visible.
| Tipus | Com funciona | Volum necessari | Exemple a Nimbus |
|---|---|---|---|
| DoS volumètric | Saturar l'amplada de banda o les connexions des d'un origen | Alt | Difícil des d'un sol origen; poc freqüent avui |
| DDoS volumètric | El mateix des de milers d'orígens distribuïts, sovint amplificant el trànsit mitjançant serveis de tercers mal configurats | Molt alt | Saturació de l'enllaç del balancejador |
| DDoS aplicatiu (capa 7) | Poques peticions, però cares: cadascuna consumeix molta CPU, memòria o base de dades | Baix | Peticions a l'informe sense paginació de 01-01 |
| Esgotament de recursos lògics | Consumir un recurs finit no tècnic | Baixíssim | Reservar i cancel·lar en bucle per omplir l'agenda d'una clínica |
El més perillós per a Nimbus és el tercer, i és el més subestimat. Recorda l'endpoint d'informes sense paginació que trencava la disponibilitat a 01-01: 20 peticions ben triades poden tombar l'API, mentre que un DDoS volumètric requereix una botnet. La defensa contra el volumètric es compra (servei anti-DDoS del proveïdor); la defensa contra l'aplicatiu es programa.
# === PATRO VULNERABLE: cost illimitat per peticio ===
@app.get("/api/v1/informes/ocupacion")
def informe(des_de: str, fins_a: str, usuari = Depends(usuari_actual)):
# El client decideix el rang: pot demanar 10 anys de dades
files = db.execute(
"SELECT * FROM reserves WHERE tenant_id = :t AND data_hora BETWEEN :d AND :h",
{"t": usuari.tenant_id, "d": des_de, "h": fins_a}).fetchall()
return [dict(f) for f in files] # i tot es materialitza en memoria# === CORRECCIO: acotar el cost ABANS d executar ===
from datetime import date, timedelta
from fastapi import HTTPException
MAX_DIES = 92 # limit de negoci: un trimestre
MAX_FILES = 5000 # limit tecnic
@app.get("/api/v1/informes/ocupacion")
@limitador.limit("5/minute") # limit de taxa per usuari
def informe(des_de: date, fins_a: date, pagina: int = 1,
usuari = Depends(usuari_actual)):
if (fins_a - des_de) > timedelta(days=MAX_DIES):
raise HTTPException(400, "El rang maxim es de 92 dies")
files = db.execute(
"""SELECT id, data_hora, servei_id, estat
FROM reserves
WHERE tenant_id = :t AND data_hora BETWEEN :d AND :h
ORDER BY data_hora
LIMIT :lim OFFSET :off""",
{"t": usuari.tenant_id, "d": des_de, "h": fins_a,
"lim": MAX_FILES, "off": (pagina - 1) * MAX_FILES}).fetchall()
return {"pagina": pagina, "dades": [dict(f) for f in files]}Les quatre defenses que apareixen aquí, explicades:
- Validació de tipus (
des_de: dateen lloc destr): rebutja entrades malformades abans d'arribar a la base de dades. - Límit de negoci (92 dies): un informe d'ocupació de deu anys no és un cas d'ús legítim. Els límits de negoci són més efectius que els tècnics perquè són defensables davant del client.
- Paginació amb
LIMIT: acota la memòria i el temps de cada petició, passi el que passi. - Límit de taxa (
5/minute): acota quantes vegades es pot pagar aquest cost. Sense ell, els altres tres límits només obliguen l'atacant a repetir més.
Detecció: latència mitjana de l'API disparada amb poc trànsit entrant, consultes lentes a PostgreSQL concentrades en un endpoint, i un mateix user_id o token concentrant la major part del consum. Aquests senyals, en un DDoS volumètric, serien diferents: molt trànsit i saturació de xarxa.
- Atacs a credencials i sessions
La identitat és avui la via d'entrada dominant, perquè una credencial vàlida no dispara cap alarma: no hi ha exploit, no hi ha programari maliciós, no hi ha anomalia de protocol. Només algú entrant.
| Atac | Mecànica | Què el fa viable | Senyal característic als registres |
|---|---|---|---|
| Força bruta | Moltes contrasenyes contra un compte | Absència de bloqueig o límit | Molts errors, un compte, poc temps |
| Atac de diccionari | Igual, però amb llistes de contrasenyes probables | Contrasenyes humanes i predictibles | Igual que l'anterior |
| Password spraying | Una contrasenya molt comuna contra molts comptes | Bloqueig per compte (que no detecta això) | Pocs errors per compte, molts comptes, mateixa IP |
| Credential stuffing | Parells correu/contrasenya de bretxes d'altres serveis | Reutilització de contrasenyes | Taxa d'èxit baixa però no nul·la; IP distribuïdes |
| Robatori de sessió / token | Fer servir un token o galeta vàlids robats | Tokens de vida llarga, sense vinculació al context | Mateix token des d'IP o país nous |
| Pass-the-cookie | Extreure la galeta de sessió del navegador de la víctima i importar-la en un altre | Galetes robades per programari maliciós salten l'MFA | Sessió sense esdeveniment d'autenticació previ |
5.1 Password spraying: el que evadeix la defensa clàssica
Mereix atenció especial perquè està dissenyat per eludir el bloqueig de compte. Si Nimbus bloqueja després de 5 intents fallits per compte, l'atacant prova una sola contrasenya molt comuna contra els 38 comptes de l'equip, espera una hora i prova la següent. Cap compte no arriba mai a 5 errors.
# Fragment del registre d autenticacio de Nimbus (illustratiu)
2026-03-14T02:14:07Z auth FAIL [email protected] ip=198.51.100.77
2026-03-14T02:14:11Z auth FAIL [email protected] ip=198.51.100.77
2026-03-14T02:14:15Z auth FAIL [email protected] ip=198.51.100.77
2026-03-14T02:14:19Z auth FAIL [email protected] ip=198.51.100.77
2026-03-14T02:14:23Z auth OK [email protected] ip=198.51.100.77Com es llegeix: quatre errors i un èxit, un intent per compte, mateixa IP, quatre segons entre cadascun, a les 2 de la matinada. Cap compte no ha arribat al llindar de bloqueig, i tanmateix l'atac ha tingut èxit. La detecció correcta no compta errors per compte, sinó errors diferents per origen i la relació entre comptes atacats i comptes existents. Aquesta és exactament la mena de regla que es construeix a 05-02.
La defensa que el neutralitza gairebé del tot és l'MFA resistent al phishing, juntament amb la comprovació de contrasenyes contra llistes de filtrades. Totes dues es desenvolupen a 02-05.
5.2 Robatori de token: per què la troballa de 01-04 era greu
A l'inventari vas descobrir un token del compte nimbus-deploy-bot sense caducitat i amb permisos d'escriptura a tots els repositoris. Un token així té tres propietats que el converteixen en l'objectiu perfecte:
- No caduca: robar-lo un cop n'hi ha prou per sempre.
- No té segon factor: un token és la credencial completa.
- És a molts llocs: a la configuració del CI, al portàtil de qui el va crear, potser en un fitxer
.env, potser a l'historial d'un xat.
La seva correcció —caducitat curta, àmbit mínim, credencials efímeres del propi CI i rotació— es detalla a 02-05.
- Atacs a aplicacions web: el Top 10 d'OWASP com a marc
L'OWASP Top 10 és la llista de les deu categories de risc més esteses en aplicacions web. No és un catàleg exhaustiu de vulnerabilitats: és una llista de categories per freqüència i impacte, i per això funciona bé com a guia de revisió. Recorrem les més rellevants per a l'API de Nimbus.
6.1 Control d'accés trencat (IDOR i companyia)
És la primera categoria del Top 10 per prevalença, i ja te la vas trobar a 01-01.
Què és. El sistema autentica correctament («sé qui ets») però no autoritza correctament («no comprovo que això sigui teu»).
# === VULNERABLE (l endpoint de 01-01) ===
@app.get("/api/v1/reservas/{reserva_id}")
def veure_reserva(reserva_id: int, usuari = Depends(usuari_actual)):
# Hi ha sessio valida, pero no es comprova de qui es la reserva
return db.execute("SELECT * FROM reserves WHERE id = :id",
{"id": reserva_id}).fetchone()# === CORREGIT ===
@app.get("/api/v1/reservas/{reserva_id}")
def veure_reserva(reserva_id: int, usuari = Depends(usuari_actual)):
fila = db.execute(
"""SELECT id, data_hora, servei_id, estat, nom_client
FROM reserves
WHERE id = :id AND tenant_id = :tenant""", # el recurs ha de ser seu
{"id": reserva_id, "tenant": usuari.tenant_id}).fetchone()
if fila is None:
raise HTTPException(404) # 404 i no 403: no revela si existeix
return dict(fila)Variants de la mateixa família que convé reconèixer:
- Escalada horitzontal: accedir a dades d'un altre usuari del mateix nivell (l'IDOR clàssic).
- Escalada vertical: un usuari de recepció aconsegueix funcions d'administrador perquè el control només és a la interfície i no al servidor.
- Manipulació de paràmetres: enviar
{"rol": "admin"}al cos d'una actualització de perfil i que el servidor ho accepti perquè fa una assignació massiva de camps.
Detecció. És difícil des de fora i fàcil des de l'auditoria: si el registre anota usuari, tenant i recurs, una consulta que busqui accessos on el tenant del recurs no coincideix amb el de l'usuari troba els intents. Sense aquest registre, és invisible. Vet aquí per què l'auditoria de 01-01 i 01-03 no era burocràcia.
6.2 Injecció SQL
Què és. La dada de l'usuari es concatena en una consulta i el motor la interpreta com a instrucció en lloc de com a dada.
# === VULNERABLE: concatenacio de cadenes ===
def buscar_clients(text, tenant_id):
consulta = f"SELECT id, nom, email FROM clients_finals " \
f"WHERE tenant_id = {tenant_id} AND nom LIKE '%{text}%'"
return db.execute(consulta).fetchall()Si text conté una cometa, l'estructura de la consulta canvia. N'hi ha prou amb aquest exemple conceptual: un valor com ' OR '1'='1 transforma la condició de cerca en una condició sempre certa, i la consulta retorna totes les files a les quals el rol de base de dades tingui accés. Variants més avançades permeten llegir altres taules o inferir dades caràcter a caràcter pel temps de resposta (injecció cega).
# === CORRECTE: consultes parametritzades ===
def buscar_clients(text, tenant_id):
return db.execute(
"""SELECT id, nom, email
FROM clients_finals
WHERE tenant_id = :t AND nom ILIKE :patro
LIMIT 100""",
{"t": tenant_id, "patro": f"%{text}%"}).fetchall()Per què la parametrització funciona de debò i l'«escapament manual» no: amb paràmetres, el motor rep primer l'estructura de la consulta i després els valors, ja com a dades. No hi ha cap contingut de text que pugui canviar l'estructura, perquè l'estructura ja està fixada. L'escapament manual, en canvi, depèn d'encertar totes les combinacions de cometes, codificacions i modes del motor: tard o d'hora falla.
Segona capa (la que salva quan la primera falla): el rol nimbus_api de 01-03 no té DELETE, no té DDL i no pot llegir nomines ni claus_passarela. Una injecció amb aquest rol és greu, però acotada. I la RLS de PostgreSQL impedeix a més que retorni files d'un altre tenant. Defensa en profunditat en estat pur.
Detecció: errors d'SQL als registres d'aplicació (un syntax error at or near és un senyal inequívoc que algú està provant), pics de consultes anòmalament lentes, i peticions amb cometes o paraules clau SQL en paràmetres que no haurien de contenir-les mai. Un WAF detecta molts intents, però no substitueix la parametrització.
6.3 Cross-Site Scripting (XSS)
Què és. L'atacant aconsegueix que el navegador d'una altra víctima executi codi en el context del lloc de Nimbus. La víctima no és el servidor: és l'usuari.
| Tipus | On viu la càrrega | Exemple a Nimbus |
|---|---|---|
| Emmagatzemat | Desada a la base de dades i servida a tothom | Camp «notes de la cita» que es mostra al tauler de la clínica |
| Reflectit | Viatja a l'URL i torna a la resposta | Cercador que mostra «No hi ha resultats per a: el que vas escriure» |
| Basat en DOM | Mai no arriba al servidor; passa al JavaScript de la SPA | La SPA llegeix un paràmetre del fragment de l'URL i l'insereix a la pàgina |
Exemple conceptual i mínim. El camp «notes» d'una reserva admet text lliure. Si la SPA l'insereix a la pàgina sense escapar, un text que contingui una etiqueta <script> s'executarà al navegador de la recepcionista de la clínica que obri aquella cita. L'impacte no és «una finestra emergent»: és que aquest codi actua amb la sessió de la recepcionista, i pot llegir l'agenda completa o fer accions en nom seu.
// === VULNERABLE: inserir HTML sense escapar ===
element.innerHTML = reserva.notes;
// === CORRECTE: inserir com a text, mai com a HTML ===
element.textContent = reserva.notes;Les tres capes de defensa contra XSS, per ordre d'importància:
- Codificació a la sortida segons el context. La mateixa dada s'escapa diferent en HTML, en un atribut, en JavaScript o en un URL. Els motors de plantilles moderns i els frameworks de SPA ho fan per defecte; el problema apareix quan algú ho desactiva a propòsit (
innerHTML,dangerouslySetInnerHTML,|safe). Content-Security-Policy: capçalera que indica al navegador de quins orígens pot carregar i executar codi. Converteix molts XSS explotables en intents fallits. Es detalla a 02-04.- Galetes
HttpOnly: impedeixen que el JavaScript llegeixi la galeta de sessió, limitant el robatori directe. No impedeixen que el codi actuï en nom de l'usuari. Es detalla a 02-05.
6.4 CSRF (falsificació de petició entre llocs)
Què és. Un lloc maliciós fa que el navegador de la víctima, ja autenticat a Nimbus, enviï una petició no desitjada. El navegador adjunta la galeta de sessió automàticament, així que la petició sembla legítima.
Exemple a Nimbus: l'administradora d'una clínica està autenticada al tauler i visita una altra pàgina que, sense que ella se n'adoni, envia un formulari a POST /api/v1/usuarios/invitar per donar d'alta un usuari nou amb permisos.
Defenses, i per què cal combinar-les:
| Defensa | Com funciona | Limitació |
|---|---|---|
Galeta SameSite=Lax o Strict |
El navegador no envia la galeta en peticions originades per un altre lloc | És la defensa principal avui; requereix navegadors moderns i no cobreix tots els casos amb Lax |
| Token anti-CSRF | Cada formulari inclou un valor impredictible que el servidor verifica | Requereix gestió d'estat |
Verificació d'Origin/Referer |
El servidor rebutja peticions d'origen diferent | Capçaleres a vegades absents |
| Autenticació per capçalera en lloc de galeta | Si el token va a Authorization, el navegador no l'adjunta sol |
Canvia el model de sessió de la SPA (vegeu 02-05) |
6.5 SSRF (falsificació de petició del costat del servidor)
Què és. L'atacant aconsegueix que el servidor de Nimbus faci una petició a un URL que ell tria. És especialment perillós al núvol, perquè el servidor és dins de la xarxa privada i pot arribar a llocs on ningú no arriba des de fora.
Exemple a Nimbus: una funcionalitat permet a la clínica indicar l'URL del seu logotip per a les factures, i el servidor la descarrega. Si no es valida, l'atacant pot indicar una adreça interna —el servei de metadades del proveïdor cloud, un tauler administratiu intern, localhost— i fer que el servidor la consulti per ell. El cas més greu és l'accés al servei de metadades de la instància, que en configuracions antigues pot retornar credencials temporals del compte cloud (A-05).
# === VULNERABLE ===
def descarregar_logo(url: str):
return requests.get(url, timeout=5).content # qualsevol URL, inclosa una interna
# === CORREGIT: llista blanca, resolucio previa i bloqueig de rangs interns ===
import ipaddress, socket
from urllib.parse import urlparse
RANGS_PROHIBITS = [ipaddress.ip_network(r) for r in
("127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12",
"192.168.0.0/16", "169.254.0.0/16", "::1/128")]
def descarregar_logo(url: str):
p = urlparse(url)
if p.scheme != "https": # 1. nomes HTTPS
raise ValueError("Esquema no permes")
ip = ipaddress.ip_address(socket.gethostbyname(p.hostname))
if any(ip in xarxa for xarxa in RANGS_PROHIBITS): # 2. res intern
raise ValueError("Desti no permes")
return requests.get(url, timeout=5, allow_redirects=False, # 3. sense redireccions
stream=True).raw.read(2_000_000) # 4. mida maximaLes quatre decisions del codi corregit: només HTTPS (evita file://, gopher:// i altres esquemes); resolució prèvia del nom i comprovació que la IP no cau en un rang intern; sense seguir redireccions, perquè una redirecció és la forma clàssica de saltar-se la comprovació; i límit de mida perquè la descàrrega no es converteixi en una denegació de servei. Tot i així, la defensa definitiva és d'arquitectura: que aquestes peticions surtin per un proxy de sortida amb llista blanca, i que el servei de metadades exigeixi la versió que requereix token (es veu a 05-07).
6.6 Deserialització insegura
Què és. L'aplicació reconstrueix objectes a partir de dades que vénen de fora. Alguns formats permeten que el procés de reconstrucció executi codi.
# === MOLT PERILLOS: pickle sobre dades externes ===
import pickle
dades = pickle.loads(cos_de_la_peticio) # pot executar codi arbitrari
# === CORRECTE: format de dades sense capacitat d execucio, i amb validacio ===
from pydantic import BaseModel
class PreferenciesClient(BaseModel):
idioma: str
zona_horaria: str
recordatoris: bool
dades = PreferenciesClient.model_validate_json(cos_de_la_peticio)La regla: pickle, yaml.load sense SafeLoader i equivalents en altres llenguatges mai no s'han d'aplicar a dades que vinguin de fora. Fes servir JSON i valida l'esquema. La validació amb un model explícit aporta a més defensa contra l'assignació massiva de camps: només s'accepten les tres claus declarades.
6.7 Exposició de dades sensibles
Menys vistosa i molt freqüent. Formes habituals en una API com la de Nimbus:
- Retornar més del necessari:
SELECT *que incloudni,notes_internesohash_password, i confiar que la interfície no els mostri. La dada viatja al client i és al navegador. - Missatges d'error verbosos: una traça completa amb la versió de la biblioteca, la ruta del fitxer i fins i tot la consulta SQL.
- Registrar el que no s'ha de registrar: tokens, contrasenyes o dades de salut als registres. Es tracta a 02-04.
- Objectes accessibles sense autorització: el bucket d'adjunts (A-02) sense política restrictiva. Ja ho vas resoldre amb URL signades de 120 s a 01-03.
6.8 Dependències i components vulnerables
L'API de Nimbus arrossega al voltant de 180 dependències transitives de Python. L'Iván ha escrit una fracció mínima del codi que s'executa a producció.
$ pip-audit
Found 3 known vulnerabilities in 2 packages
Name Version ID Fix Versions
----------- -------- ------------------ ------------
py-lib-x 2.4.1 GHSA-xxxx-xxxx-01 2.4.3
py-lib-x 2.4.1 GHSA-xxxx-xxxx-02 2.5.0
py-lib-y 1.9.0 PYSEC-2026-0001 1.9.4Com es llegeix i què es fa: cada línia és una vulnerabilitat coneguda amb la seva versió corregida. L'important no és la llista, és el procés: que aquesta ordre s'executi a cada construcció, que trenqui la integració quan aparegui alguna cosa de severitat alta i que existeixi un compromís explícit de termini de correcció. Una anàlisi que ningú no llegeix és pitjor que cap, perquè genera la sensació d'estar cobert. El yaml complet del CI és a 02-04.
- Programari maliciós en acció: ransomware modern i doble extorsió
A 01-02 vas veure la taxonomia de nou famílies de programari maliciós. Aquí interessa com opera avui la que més dany causa a empreses de la mida de Nimbus.
El que va canviar respecte al ransomware clàssic: ja no és un fitxer que xifra un ordinador. És una operació humana, amb diversos grups especialitzats, que dura setmanes i en la qual el xifratge és l'últim pas.
7.1 Cronologia típica
| Moment | Què passa | Detectable? |
|---|---|---|
| Dia 0 | Accés inicial: phishing, credencial de VPN sense MFA o servei exposat sense pedaç. Sovint l'obté un intermediari que ven l'accés | Sí: autenticació anòmala, execució inusual |
| Dies 1-3 | Reconeixement intern: mapa de la xarxa, on són les còpies, qui és administrador | Sí: consultes al directori, escaneig intern |
| Dies 3-10 | Escalada i moviment lateral; s'apoderen de credencials administratives | Sí: ús d'eines administratives fora del que és habitual |
| Dies 10-20 | Exfiltració: s'emporten les dades abans de xifrar. És la base de la segona extorsió | Sí: volum de sortida anòmal cap a destinacions noves |
| Dia 20 | Sabotatge de la recuperació: esborrament de còpies, instantànies i rèpliques. Aquest pas decideix el desenllaç | Sí, i és l'última oportunitat |
| Dia 21 | Xifratge, normalment un divendres a la nit o en festiu, i nota de rescat | Massa tard |
| Després | Doble extorsió: pagar per desxifrar i perquè no es publiquin les dades. A vegades triple: s'avisa els clients de la víctima | — |
Les tres conclusions defensives que es deriven d'aquesta cronologia:
- Hi ha tres setmanes de finestra. El ransomware no és instantani: és la conclusió visible d'un compromís llarg. Cada dia d'aquesta finestra és una oportunitat de detecció que Nimbus avui no aprofita perquè ningú no mira els registres.
- Les còpies són l'objectiu, no un dany col·lateral. L'atacant busca activament les còpies i les esborra. Per això una còpia accessible amb les mateixes credencials que producció no és una còpia: és un fitxer més que es xifrarà. La resposta és la immutabilitat i la separació de comptes (02-04, 04-06).
- Pagar no resol la fuita. Encara que es pagui i es desxifri, les dades ja van sortir. Per a Nimbus, amb dades de cites de clíniques, això significa notificació de bretxa amb independència de si es recupera el servei. Les implicacions concretes es veuen a 06-03, i la decisió de pagament s'analitza en el cas de Colonial Pipeline a 02-06.
Nota legal: el pagament de rescats té implicacions legals, fiscals i de sancions que varien segons la jurisdicció i la identitat del grup atacant. Qualsevol decisió en un cas real s'ha de consultar amb assessoria jurídica i amb les autoritats competents.
- Atacs a la cadena de subministrament
Què és. En lloc d'atacar Nimbus directament, l'adversari compromet alguna cosa en què Nimbus confia i espera que Nimbus la instal·li o l'executi. És un atac d'eficiència extrema: es compromet un i s'arriba a milers.
| Vector | Com arriba a Nimbus | Exemple conceptual |
|---|---|---|
| Paquet de codi compromès | Una dependència legítima rep una versió amb codi maliciós | Una biblioteca de Python usada per l'API publica una versió troianitzada |
| Confusió de dependències | Un paquet públic amb el mateix nom que un d'intern té versió major i el gestor el prefereix | nimbus-utils intern davant d'un nimbus-utils públic maliciós |
| Acció de CI/CD de tercers | Un pas del flux de GitHub Actions es referencia per etiqueta mòbil i aquesta etiqueta es reapunta | Una acció de desplegament que roba els secrets de l'entorn |
| Proveïdor amb accés | El proveïdor és compromès i el seu accés legítim es fa servir contra el client | La consultora de sistemes (A-19) |
| Actualització de programari signada | El mateix fabricant distribueix una actualització compromesa | El cas SolarWinds, que analitzarem a 02-06 |
Per què és tan difícil de detectar: tot l'anterior sembla legítim. Una dependència instal·lada per pip des de l'índex oficial, una acció de CI amb nom conegut, una connexió de la consultora en horari laboral. No hi ha cap anomalia de protocol per detectar; només comportament posterior.
Defenses aplicables avui a Nimbus:
- Fixar versions exactes i un fitxer de bloqueig amb sumes de verificació, perquè la construcció sigui reproduïble.
- Fixar les accions de CI per hash del commit, no per etiqueta:
# Vulnerable: l etiqueta pot reapuntar-se a un altre codi
- uses: alguna-org/accio-desplegament@v3
# Robust: el hash identifica un codi concret i immutable
- uses: alguna-org/accio-desplegament@a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0- Mínim privilegi al CI: que el flux de construcció no tingui accés als secrets de producció, i que els secrets siguin efímers.
- Generar un inventari de components (SBOM) per poder respondre en minuts a «fem servir la biblioteca que acaba de sortir a les notícies?». És l'inventari de 01-04 aplicat al programari.
El tractament del proveïdor com a risc gestionable —avaluació, contracte, revisió— correspon a 04-04.
- Atacs específics a API
L'API de Nimbus (A-04) és la seva superfície principal: ~120 endpoints usats per la SPA i l'app mòbil. Els atacs a API no sempre són «vulnerabilitats» en el sentit clàssic; moltes vegades són usos legítims portats a l'extrem.
| Atac | Què explota | Exemple a Nimbus | Defensa |
|---|---|---|---|
| Enumeració d'identificadors | ID seqüencials i respostes que distingeixen «no existeix» de «no és teu» | Recórrer /api/v1/reservas/{id} incrementant el número |
Identificadors no endevinables (UUID); respondre sempre 404; límit de taxa |
| Enumeració d'usuaris | Diferències en el missatge o en el temps de resposta en iniciar sessió o en recuperar contrasenya | «Aquest correu no està registrat» davant de «Contrasenya incorrecta» | Resposta i temps idèntics en tots dos casos |
| Abús de lògica de negoci | Regles del negoci, no fallades de codi | Reservar 200 buits i cancel·lar-los, blocant l'agenda d'una clínica; aplicar un descompte repetidament | Límits per usuari i per tenant; validació de regles al servidor; detecció de patrons |
| Manca de límit de taxa | Absència de cost per a l'atacant | Provar contrasenyes, exportar dades, esgotar el saldo d'SMS | Límit per IP, per usuari i per token; quotes per tenant |
| Exposició excessiva de dades | El servidor retorna tot i el client filtra | L'objecte de reserva inclou dni i notes_internes |
Esquemes de resposta explícits, mai SELECT * |
| Endpoints a l'ombra | Versions antigues o de proves que continuen vives | /api/v0/ sense les validacions noves |
Inventari d'endpoints; retirada explícita de versions |
| Consum insegur d'API de tercers | Es confia cegament en la resposta del tercer | Acceptar un webhook de la passarel·la sense verificar la signatura | Verificació de signatura; validar també el que ve d'un soci |
El punt que més se subestima: l'abús de lògica de negoci. No hi ha cap vulnerabilitat tècnica a reservar i cancel·lar 200 cites; el sistema fa exactament el que se li va demanar. Cap WAF, cap anàlisi estàtica i cap escàner no ho detecten, perquè no hi ha res malformat. Només ho detecten límits de negoci pensats per algú que coneix el domini, i alertes sobre mètriques de negoci (taxa de cancel·lació anòmala en un tenant). És la raó per la qual la seguretat no es pot delegar del tot a les eines.
- Un atac encadenat sobre Nimbus, pas a pas
Els atacs reals no són una tècnica: són una cadena en què cada baula aprofita l'anterior. Reconstruïm-ne un de complet, connectant el que hem vist a la lliçó.
flowchart TB
A["1. RECONEIXEMENT\nOSINT: es localitza en un repositori\npublic un fitxer .env antic\namb un token d API sense caducitat\n(troballa de 01-04)"]
A --> B["2. VALIDACIO\nEl token continua actiu: ningu\nno l ha rotat ni li ha posat caducitat"]
B --> C["3. ACCES INICIAL\nPeticions a l API de produccio\namb un token legitim:\nsense exploit, sense alarma"]
C --> D["4. DESCOBRIMENT\nEnumeracio d endpoints\ni prova de l IDOR a\n/api/v1/reservas/{id}"]
D --> E["5. RECOLLIDA\nExportacio en lots petits\ndurant tres setmanes\nper no cridar l atencio"]
E --> F["6. ESCALADA\nA les dades apareix un URL\nsignat del bucket d adjunts;\ns intenta ampliar l acces"]
F --> G["7. EXFILTRACIO\nSortida de dades de cites\ncap a un emmagatzematge extern"]
G --> H["8. IMPACTE\nDades que revelen salut fora\nde control + notificacio de bretxa\n+ perdua de confianca"]
On es trenca aquesta cadena, baula per baula:
| Baula | Control que la trenca | On s'estudia |
|---|---|---|
| 1 | Anàlisi de secrets al CI i a l'històric del repositori | 02-04 |
| 2 | Caducitat obligatòria de tots els tokens i rotació periòdica | 02-05 |
| 3 | Àmbit mínim del token i restricció d'origen; alerta per ús des d'IP nova | 02-05, 05-02 |
| 4 | Filtre per tenant_id i RLS (ja corregit a 01-01 i 01-03); ID no endevinables |
02-04 |
| 5 | Límit de taxa per token i alerta per volum d'exportació anòmal | 02-04, 05-02 |
| 6 | URL signades de 120 s (ja implantat) i bucket privat | Ja resolt a 01-03 |
| 7 | Filtratge de sortida i detecció de transferències anòmales | 05-02, 05-04 |
| 8 | Xifratge i minimització redueixen l'impacte residual | Mòdul 3, 06-03 |
L'observació decisiva: de les vuit baules, cinc es trenquen amb mesures que costen poc o res —caducitat de tokens, àmbit mínim, filtre de tenant, límit de taxa i una alerta de volum—. Cap no requereix comprar un producte. I la baula 6 ja estava trencada d'antuvi gràcies a una decisió de disseny presa a 01-03: les URL signades de vida curta. Això és exactament el que significa «defensa en profunditat» quan funciona.
- Taula resum: atac, propietat afectada, detecció i defensa
| Atac | Propietat CIA afectada | Senyal de detecció | Defensa principal | Lliçó |
|---|---|---|---|---|
| OSINT | — (habilitador) | No detectable als teus sistemes | Reduir el que es publica; rotar secrets exposats | 01-04, 02-04 |
| Escaneig | — (habilitador) | Ràfegues de 404 a rutes inexistents | Reduir superfície; alerta si una ruta sensible deixa de donar 404 | 02-04, 05-01 |
| Sniffing / MitM | Confidencialitat, integritat | Certificat inesperat; ARP anòmal | TLS amb verificació estricta; xifratge també a l'intern | 02-04, mòdul 3 |
| DNS spoofing | Autenticitat | Resolucions fora del rang esperat | DNSSEC, resolutors controlats, MFA al compte de DNS | 05-04 |
| DDoS volumètric | Disponibilitat | Saturació de xarxa amb trànsit massiu | Servei anti-DDoS del proveïdor; CDN | 02-04 |
| DDoS aplicatiu | Disponibilitat | Latència alta amb poc trànsit; un endpoint concentra el cost | Paginació, límits de negoci, límit de taxa | 02-04, 05-05 |
| Força bruta / diccionari | Confidencialitat | Molts errors en un compte | Límit d'intents; MFA | 02-05 |
| Password spraying | Confidencialitat | Pocs errors en molts comptes, mateixa IP | Detecció per origen; MFA; contrasenyes no filtrades | 02-05 |
| Credential stuffing | Confidencialitat | Èxits aïllats des d'IP distribuïdes | MFA; comprovació contra llistes filtrades | 02-05 |
| Robatori de sessió / pass-the-cookie | Confidencialitat, autenticitat | Token usat des de context nou sense autenticació prèvia | Galetes segures, vida curta, revocació, vinculació al context | 02-05 |
| IDOR / control d'accés trencat | Confidencialitat, integritat | Accessos on el tenant del recurs ≠ tenant de l'usuari | Autorització per recurs; RLS; ID no endevinables | 01-01, 01-03 |
| Injecció SQL | Totes | Errors de sintaxi SQL als registres; consultes anòmales | Consultes parametritzades + rol de mínim privilegi | 02-04, 05-05 |
| XSS | Confidencialitat, integritat | Informes de CSP; contingut amb etiquetes en camps de text | Codificació a la sortida + CSP + HttpOnly |
02-04, 05-05 |
| CSRF | Integritat | Accions amb Origin extern |
SameSite; token anti-CSRF |
02-04, 02-05 |
| SSRF | Confidencialitat | Peticions sortints a rangs interns | Llista blanca, bloqueig de rangs interns, proxy de sortida | 02-04, 05-07 |
| Deserialització insegura | Totes | Execució inesperada després de rebre dades | JSON + validació d'esquema; mai pickle extern |
05-05 |
| Exposició de dades sensibles | Confidencialitat | Respostes amb camps de més; traces en errors | Esquemes de sortida explícits; errors genèrics | 02-04 |
| Dependències vulnerables | Totes | Informe d'anàlisi de composició | Anàlisi al CI + termini de correcció + SBOM | 02-04 |
| Ransomware | Disponibilitat, confidencialitat | Moviment lateral, esborrament de còpies, xifratge massiu | Còpies immutables, MFA, segmentació, EDR | 02-04, 04-06 |
| Cadena de subministrament | Totes | Comportament nou després d'una actualització | Versions fixades per hash, CI sense secrets de producció, SBOM | 02-04, 04-04 |
| Abús de lògica de negoci | Integritat, disponibilitat | Mètriques de negoci anòmales per tenant | Límits de negoci; alertes sobre mètriques pròpies | 02-04 |
| Enumeració | Confidencialitat | Recorregut seqüencial d'ID; molts 404 autenticats | UUID, respostes uniformes, límit de taxa | 02-05 |
Errors Comuns i Consells
Errors comuns:
- Estudiar els atacs com una llista de noms. El que cal retenir de cadascun són tres coses: quina propietat trenca, quin senyal deixa i quin control el neutralitza. El nom és el de menys.
- Creure que HTTPS resol els atacs de xarxa. Resol l'escolta del contingut extern. No cobreix el trànsit intern en clar, ni les metadades, ni l'usuari que accepta un certificat no vàlid.
- Confiar el filtratge d'entrades a una llista negra. Intentar prohibir
',<script>oUNIONsempre es pot eludir amb codificacions. La defensa correcta és estructural: parametritzar consultes i codificar a la sortida. - Pensar que un WAF substitueix el codi correcte. Un WAF és una capa útil que dona temps; no arregla una injecció ni un control d'accés trencat.
- Ignorar el DDoS aplicatiu. S'inverteix en protecció volumètrica mentre un sol endpoint sense paginació permet tombar el servei des d'un portàtil.
- Tractar el ransomware com un problema d'antivirus. És una operació de setmanes el desenllaç de la qual el decideixen les còpies immutables, l'MFA i la segmentació, no la signatura del fitxer final.
- Suposar que les dependències són problema d'altres. El 95 % del codi que corre a producció no l'ha escrit l'Iván, i les vulnerabilitats d'aquest 95 % són igual d'explotables.
- Oblidar la lògica de negoci. Cap eina no detecta l'abús d'una funcionalitat que funciona exactament com es va dissenyar.
Consells:
- Quan revisis un endpoint, fes-te quatre preguntes en aquest ordre: qui ets? (autenticació), és teu? (autorització), és vàlid el que envies? (validació) i quant costa això? (límits).
- Busca al repositori de Nimbus aquests cinc patrons:
verify=False,SELECT *, concatenació de cadenes en SQL,innerHTMLipickle.loads. Hi trobaràs la major part del risc d'aplicació en menys d'una hora. - Per a cada atac nou que aprenguis, escriu la consulta de registre que el detectaria. Si no la pots escriure, no el detectaràs.
- Practica l'exercici de la cadena: agafa qualsevol incident i pregunta't en quina baula hauria estat més barat trencar-lo. Gairebé sempre és una de les primeres.
- Recorda que la majoria dels atacs amb èxit no fan servir exploits: fan servir credencials vàlides i configuracions oblidades.
Exercicis
Exercici 1 — Classificar i respondre a sis senyals
Per a cadascun d'aquests extractes o fets observats a Nimbus, indica: quin atac suggereix, quina propietat CIA hi ha en joc, què comprovaries a continuació i quina defensa correspon.
(a) 2026-04-02T03:22:10Z auth FAIL usuari=ivan@... ip=192.0.2.9
2026-04-02T03:22:14Z auth FAIL usuari=lucia@... ip=192.0.2.9
2026-04-02T03:22:18Z auth FAIL usuari=sara@... ip=192.0.2.9
2026-04-02T03:22:22Z auth FAIL usuari=marta@... ip=192.0.2.9(b) ERROR sqlalchemy.exc.ProgrammingError: syntax error at or near "OR"
LINE 1: ...clients_finals WHERE tenant_id = 42 AND nom LIKE '%' OR '1'='1...(c) GET /api/v1/reservas/10001 200
GET /api/v1/reservas/10002 200
GET /api/v1/reservas/10003 200
GET /api/v1/reservas/10004 200
... 4.800 peticions en 40 minuts, mateix token, respostes 200(d) L API de Nimbus registra 34 peticions a /api/v1/informes/ocupacion
en 90 segons. La CPU de PostgreSQL esta al 100 %. El transit entrant
total es de 0,4 Mbps.(e) El servidor de l API ha fet peticions sortints a
http://169.254.169.254/latest/meta-data/iam/security-credentials/
just despres que una clinica configures l URL del seu logotip.(f) Un desplegament rutinari canvia una accio del flux de GitHub Actions
referenciada com a @v3. Despres del desplegament, el flux fa una peticio
sortint a un domini mai vist abans.Exercici 2 — Corregir tres fragments vulnerables
Els fragments següents són al codi de Nimbus. Per a cadascun: identifica la vulnerabilitat, explica com s'explotaria conceptualment, reescriu el codi corregit i afegeix una segona capa de defensa independent del codi.
# (a) Cercador de clients del tauler de la clinica
@app.get("/api/v1/clientes/buscar")
def buscar(q: str, usuari = Depends(usuari_actual)):
sql = f"SELECT * FROM clients_finals WHERE tenant_id={usuari.tenant_id} " \
f"AND nom LIKE '%{q}%'"
return [dict(f) for f in db.execute(sql).fetchall()]# (b) Descarrega de l adjunt d una reserva
@app.get("/api/v1/adjuntos/{clau}")
def adjunt(clau: str, usuari = Depends(usuari_actual)):
return storage.descarregar(f"nimbus-adjuntos-prod/{clau}")# (c) Recepcio del webhook de la passarela de pagament
@app.post("/api/v1/webhooks/pago")
def webhook(esdeveniment: dict):
if esdeveniment["estat"] == "pagat":
db.execute("UPDATE factures SET estat='pagada' WHERE id=:id",
{"id": esdeveniment["factura_id"]})
return {"ok": True}Exercici 3 — Reconstruir i trencar una cadena
Nimbus pateix l'incident següent:
- El Rubén rep un correu amb un fitxer adjunt que diu ser una incidència d'una clínica client. L'obre.
- El seu portàtil executa un component que estableix un canal sortint amb l'atacant.
- L'atacant extreu del navegador del Rubén la galeta de sessió del tauler intern de suport, que no caduca fins passats 30 dies.
- Amb aquesta galeta entra al tauler de suport sense passar per l'inici de sessió ni per l'MFA.
- Des del tauler de suport, que permet «veure com a client», accedeix a les dades de 40 clíniques.
- Exporta un CSV amb 120.000 registres de cites fent servir la funcionalitat d'exportació del tauler.
- Tres dies després, l'exportació apareix publicada en un fòrum.
Es demana: (a) assigna cada pas a la seva fase de la Kill Chain i a la tècnica del catàleg d'aquesta lliçó; (b) indica dos controls per pas que l'haurien trencat; (c) explica per què l'MFA no va protegir al pas 4 i quina mesura concreta sí que ho hauria fet; (d) digues en quin pas la detecció hauria estat més fàcil i quin senyal exacte hauries buscat.
Solucions
Solució 1
| Cas | Atac | Propietat | Què comprovar | Defensa |
|---|---|---|---|---|
| (a) | Password spraying: un intent per compte, molts comptes, mateixa IP, matinada | Confidencialitat | Si hi va haver algun auth OK des d'aquesta IP; si aquests comptes tenen MFA; si la IP apareix en altres registres |
MFA obligatori; detecció per origen (no per compte); bloqueig progressiu de la IP; contrasenyes comprovades contra llistes filtrades |
| (b) | Injecció SQL: l'error revela que l'entrada de l'usuari va alterar la sintaxi | Totes | Quin endpoint el va generar, quin paràmetre, si alguna variant va tenir èxit (respostes 200 amb volum anòmal), quins permisos té el rol de BD | Parametritzar la consulta; rol nimbus_api sense DELETE ni DDL; errors genèrics cap al client; alerta sobre errors de sintaxi SQL |
| (c) | Enumeració d'identificadors amb IDOR: ID seqüencials retornant 200 | Confidencialitat | Si els ID pertanyen a diversos tenants (si sí, el control d'accés està trencat); quin token és i a qui pertany | Filtre per tenant_id i RLS; identificadors UUID; límit de taxa per token; alerta pel nombre de recursos diferents accedits per sessió |
| (d) | DDoS aplicatiu: cost altíssim amb trànsit ridícul | Disponibilitat | Quin rang de dates demanen aquestes peticions; si vénen d'un sol token o tenant | Paginació obligatòria, límit de rang, límit de taxa, temps màxim de consulta a PostgreSQL |
| (e) | SSRF apuntant al servei de metadades de la instància; objectiu: credencials temporals d'A-05 | Confidencialitat (greu: pot escalar a tot) | Si la petició va tenir èxit i si aquestes credencials s'han fet servir des de fora; revisar el registre del compte cloud | Validació d'URL amb bloqueig de rangs interns i sense redireccions; proxy de sortida amb llista blanca; exigir la versió del servei de metadades que requereix token |
| (f) | Cadena de subministrament via acció de CI referenciada per etiqueta mòbil | Totes | Quins secrets eren accessibles en aquest flux; què es va enviar al domini nou; rotar tot el que estigui exposat immediatament | Fixar accions per hash de commit; flux sense accés a secrets de producció; credencials efímeres; filtratge de sortida de l'executor |
Solució 2
(a) Cercador de clients. Vulnerabilitats: injecció SQL per concatenació i exposició excessiva per SELECT *. Explotació conceptual: un valor de q amb una cometa tanca la cadena i permet afegir condicions, convertint el filtre en una condició sempre certa i retornant el contingut complet de la taula accessible al rol.
@app.get("/api/v1/clientes/buscar")
@limitador.limit("30/minute")
def buscar(q: str, usuari = Depends(usuari_actual)):
if len(q) < 3:
raise HTTPException(400, "Introdueix almenys 3 caracters")
files = db.execute(
"""SELECT id, nom, email
FROM clients_finals
WHERE tenant_id = :t AND nom ILIKE :patro
ORDER BY nom LIMIT 50""",
{"t": usuari.tenant_id, "patro": f"%{q}%"}).fetchall()
return [dict(f) for f in files]Segona capa independent del codi: el rol nimbus_api sense DELETE ni DDL (01-03) i la RLS per tenant_id, que impedeix retornar files alienes encara que la consulta es manipuli. El LIMIT 50 afegeix a més protecció contra l'extracció massiva i el mínim de 3 caràcters evita el cercador buit que retorna tota la base.
(b) Descàrrega d'adjunts. Vulnerabilitat: control d'accés trencat — es descarrega qualsevol clau del bucket sense comprovar que la reserva pertany al tenant de l'usuari. A més pot permetre recorregut de rutes si la clau conté ../. Explotació conceptual: coneguda o endevinada una clau, qualsevol usuari autenticat descarrega l'adjunt d'una altra clínica: un informe mèdic escanejat.
@app.get("/api/v1/adjuntos/{reserva_id}")
def adjunt(reserva_id: int, usuari = Depends(usuari_actual)):
fila = db.execute(
"SELECT adjunt_clau FROM reserves WHERE id=:id AND tenant_id=:t",
{"id": reserva_id, "t": usuari.tenant_id}).fetchone()
if fila is None or fila.adjunt_clau is None:
raise HTTPException(404)
url = generar_url_signada("nimbus-adjuntos-prod", fila.adjunt_clau,
caducitat_segons=120)
registrar_auditoria(usuari.id, "descarrega_adjunt", reserva_id)
return {"url": url}Canvis clau: el client ja no tria la clau de l'objecte, sinó l'identificador de la seva reserva; la clau real s'obté de la base de dades després de verificar-ne la propietat; es lliura un URL signat de 120 s en lloc de servir l'objecte; i es registra l'accés. Segona capa: el bucket és privat, la política impedeix l'accés anònim i l'API no té permís d'esborrament sobre ell (01-03).
(c) Webhook de pagament. Vulnerabilitats: suplantació (no es verifica que el missatge vingui de la passarel·la) i manipulació de la integritat (qualsevol pot marcar factures com a pagades). Explotació conceptual: una petició HTTP a aquest endpoint amb un JSON adequat converteix una factura impagada en pagada. És l'amenaça S del flux F8 del DFD de 01-04.
import hmac, hashlib
from fastapi import Request, HTTPException
@app.post("/api/v1/webhooks/pago")
async def webhook(peticio: Request):
cos = await peticio.body()
signatura_rebuda = peticio.headers.get("X-Pasarela-Firma", "")
signatura_esperada = hmac.new(SECRET_WEBHOOK, cos, hashlib.sha256).hexdigest()
# Comparacio en temps constant: evita deduir la signatura mesurant temps
if not hmac.compare_digest(signatura_rebuda, signatura_esperada):
registrar_auditoria(None, "webhook_signatura_invalida", peticio.client.host)
raise HTTPException(401)
esdeveniment = json.loads(cos)
if esdeveniment["tipus"] != "pago.confirmado":
return {"ok": True}
# Idempotencia: el mateix esdeveniment pot arribar dues vegades
db.execute("""INSERT INTO esdeveniments_passarela (id_esdeveniment) VALUES (:e)
ON CONFLICT DO NOTHING""", {"e": esdeveniment["id"]})
...Segona capa: confirmar l'estat contra l'API de la passarel·la abans de donar per bona una operació econòmica, i restringir l'origen a les IP publicades pel proveïdor. L'HMAC s'explica en detall a 03-04.
Solució 3
(a) i (b) Cadena, tècniques i controls:
| Pas | Fase | Tècnica | Control 1 | Control 2 |
|---|---|---|---|---|
| 1. Correu amb adjunt | Lliurament | Phishing amb adjunt (02-03) | Filtratge de correu amb anàlisi d'adjunts i aïllament | Bàner de correu extern + formació i canal de report |
| 2. Execució i canal sortint | Explotació + Comandament i control | Execució a l'endpoint | EDR amb detecció de comportament | Filtratge de sortida: només destinacions permeses des de portàtils |
| 3. Robatori de galeta del navegador | Credential Access | Pass-the-cookie | Sessions de vida curta (hores, no 30 dies) | Xifratge del magatzem del navegador + política de sessió reautenticada |
| 4. Ús de la galeta | Accés inicial a l'aplicació | Robatori de sessió | Vinculació de la sessió al context (IP/dispositiu) i reautenticació davant de canvi | Alerta per sessió activa sense esdeveniment d'autenticació previ |
| 5. «Veure com a client» al tauler de suport | Escalada / Privilege Escalation | Funció administrativa sense control | Reautenticació i justificació obligatòria per suplantar; registre nominal | Aprovació d'un segon operador i accés just-in-time (02-05) |
| 6. Exportació de 120.000 registres | Recollida | Exportació massiva | Límit de volum per exportació i per dia | Alerta immediata sobre qualsevol exportació per sobre d'un llindar |
| 7. Publicació | Impacte | Divulgació | Xifratge i minimització redueixen el dany residual | Pla de resposta i notificació (04-05, 06-03) |
(c) Per què l'MFA no va protegir. L'MFA es verifica en el moment de l'inici de sessió i produeix una sessió. Si l'atacant roba la sessió ja emesa, no torna a passar per l'autenticació: entra directament amb un artefacte vàlid. Aquesta és l'essència del pass-the-cookie i la raó per la qual «tenim MFA» no és una resposta completa.
El que sí que hauria funcionat, per ordre d'eficàcia:
- Vincular la sessió a un context (dispositiu, IP o certificat de client), de manera que una galeta usada des d'un altre equip s'invalidi.
- Sessions curtes amb renovació silenciosa: una galeta de 30 dies és una credencial permanent disfressada. Hores, no setmanes.
- Reautenticació per a operacions sensibles: suplantar un client o exportar dades exigeix tornar a presentar el segon factor, encara que la sessió sigui vàlida.
- Enllaç criptogràfic del token al client (token binding / claus lligades al dispositiu), que és la línia cap a la qual apunten les passkeys (02-05).
(d) On detectar i quin senyal buscar. El punt més fàcil és el pas 6: una exportació de 120.000 registres que abasta 40 tenants diferents és un esdeveniment quantitativament únic en l'operació normal de Nimbus. La consulta de detecció és directa sobre la taula d'auditoria append-only de 01-01:
-- Exportacions anomales: mes de 5.000 registres o mes de 3 tenants
-- diferents tocats per la mateixa sessio en una hora
SELECT sessio_id, usuari_id,
COUNT(DISTINCT tenant_id) AS tenants,
SUM(num_registres) AS registres
FROM auditoria_accessos
WHERE accio = 'exportacio'
AND ts > now() - interval '1 hour'
GROUP BY sessio_id, usuari_id
HAVING SUM(num_registres) > 5000
OR COUNT(DISTINCT tenant_id) > 3;L'important d'aquesta consulta no és el seu SQL, sinó la seva premissa: només funciona si l'auditoria registra sessio_id, tenant_id i num_registres. La detecció no s'improvisa quan passa l'incident; es dissenya quan s'escriu el registre. Un segon senyal útil, més primerenc, és el pas 4: una sessió activa sense esdeveniment d'autenticació previ al registre és una anomalia que no té explicació legítima.
Conclusió
Ja coneixes l'adversari en acció. Has recorregut els atacs ordenats per les fases de la cadena: el reconeixement que passa sense tocar els teus sistemes —OSINT en certificats, repositoris i ofertes de feina— i l'escaneig que sí que deixa rastre, amb la lliçó pràctica que l'alerta útil no és «ens escanegen» sinó «una ruta sensible ha deixat de retornar 404». Has vist els atacs a la xarxa —escolta, ARP i DNS spoofing, interposició— i per què continuen important en un món amb HTTPS: pel trànsit intern en clar, per les metadades i pel verify=False que algú va deixar a producció. Has distingit la denegació de servei volumètrica, en què es compra defensa, de l'aplicativa, que es programa, i has comprovat que vint peticions ben triades fan més mal que una botnet quan falta la paginació.
En credencials has après a distingir força bruta, diccionari, password spraying —dissenyat precisament per eludir el bloqueig per compte—, credential stuffing i robatori de sessió, amb la idea incòmoda que una credencial vàlida no dispara cap alarma. En aplicacions web has recorregut el Top 10 d'OWASP sobre l'API de Nimbus: control d'accés trencat i IDOR, injecció SQL i per què la parametrització funciona on l'escapament manual falla, XSS amb les seves tres capes de defensa, CSRF, SSRF i la seva ruta cap a les credencials del compte cloud, deserialització insegura, exposició de dades i dependències vulnerables. Has entès el ransomware modern com una operació de tres setmanes el desenllaç de la qual el decideixen les còpies immutables, no l'antivirus; els atacs a la cadena de subministrament, on tot sembla legítim; i els atacs a API, amb l'abús de lògica de negoci que cap eina no detecta. I has reconstruït una cadena completa sobre Nimbus, comprovant que cinc de les seves vuit baules es trenquen amb mesures que no costen diners.
Falta el vector que encapçala gairebé totes les estadístiques i que no apareix en cap de les tècniques anteriors: la persona. A la lliçó següent, Enginyeria Social i Phishing (02-03), veurem per què el factor humà és la via dominant, quins principis d'influència exploten els atacants, el catàleg complet de tècniques —del phishing massiu al frau del CEO dirigit a la Sara, el vishing, el quishing i el consent phishing—, com desmuntar un correu maliciós indicador per indicador llegint-ne les capçaleres, i les defenses tècniques i de procés que el contraresten, començant per SPF, DKIM i DMARC.
Curs de Fonaments de Seguretat Informàtica
Mòdul 1: Introducció a la Seguretat Informàtica
- Conceptes Bàsics de Seguretat Informàtica
- Tipus d'Amenaces i Vulnerabilitats
- Principis de la Seguretat Informàtica
- Actius, Superfície d'Atac i Actors d'Amenaça
Mòdul 2: Ciberseguretat
- Definició i Abast de la Ciberseguretat
- Tipus d'Atacs Cibernètics
- Enginyeria Social i Pesca de Credencials
- Mesures de Protecció en Ciberseguretat
- Identitat, Autenticació i Control d'Accés
- Casos d'Estudi d'Incidents de Ciberseguretat
Mòdul 3: Criptografia
- Introducció a la Criptografia
- Criptografia Simètrica
- Criptografia Asimètrica
- Funcions Hash, HMAC i Emmagatzematge de Contrasenyes
- Protocols Criptogràfics
- Gestió de Claus, Certificats i PKI
- Aplicacions de la Criptografia
Mòdul 4: Gestió de Riscos i Mesures de Protecció
- Avaluació de Riscos
- Polítiques de Seguretat
- Controls de Seguretat
- Risc de Tercers i Cadena de Subministrament
- Pla de Resposta a Incidents
- Recuperació davant Desastres i Continuïtat de Negoci
Mòdul 5: Eines i Tècniques de Seguretat
- Eines d'Anàlisi de Vulnerabilitats
- Tècniques de Monitoratge i Detecció
- Proves de Penetració
- Seguretat en Xarxes
- Seguretat en Aplicacions
- Enfortiment de Sistemes i Seguretat de l'Endpoint
- Seguretat al Núvol i en Contenidors
Mòdul 6: Bones Pràctiques i Normatives
- Bones Pràctiques en Seguretat Informàtica
- Normatives i Estàndards de Seguretat
- Protecció de Dades Personals i RGPD a la Pràctica
- Compliment i Auditoria
- Formació i Sensibilització
- Ètica, Aspectes Legals i Divulgació Responsable
