La lliçó anterior va tancar amb dotze mesures prioritzades per a Nimbus, i la número u era l'MFA. No va ser casualitat: diverses de les restants giren també al voltant de la mateixa pregunta —qui ets i què pots fer—. Quan la meitat de la plantilla treballa fora de l'oficina, les dades són al núvol, hi ha una consultora amb accés remot i l'API la consumeix una app mòbil, el perímetre deixa de ser una línia a la xarxa. El que queda separant un atacant de les agendes de les clíniques és una credencial i una decisió d'autorització. Aquesta lliçó desenvolupa totes dues: el cicle de vida d'una identitat i els comptes orfes, què diuen avui les guies sobre contrasenyes, les formes d'MFA ordenades per la seva resistència real al phishing, SSO i els protocols d'identitat amb la diferència exacta entre OAuth 2.0 i OpenID Connect, sessions i JWT amb la seva validació correcta davant de la ingènua, els cinc models d'autorització amb el disseny concret de Nimbus, i els comptes privilegiats amb accés just-in-time i recertificació.
Contingut
- La identitat com a nou perímetre
- Cicle de vida de la identitat i comptes orfes
- Factors d'autenticació i les seves debilitats
- Contrasenyes: què diuen avui les guies
- MFA ordenat per resistència al phishing
- SSO, federació i protocols d'identitat
- Sessions i tokens: galetes i JWT
- Models d'autorització i l'RBAC de Nimbus
- Multi-tenència i aïllament
- Comptes privilegiats, de servei i de tercers
- Recertificació: la revisió periòdica d'accessos
- La identitat com a nou perímetre
El model clàssic dividia el món en dins i fora, i posava el control a la frontera. Aquest model descriu cada vegada pitjor la realitat de Nimbus:
| Element | On és | Ho cobreix un perímetre de xarxa? |
|---|---|---|
| Base de dades i buckets (A-01, A-02, A-03) | Núvol del proveïdor (A-05) | No |
| 19 empleats en remot | Cases, cafeteries, trens | No |
| App mòbil dels clients | Milers de dispositius aliens | No |
| Repositori i CI/CD (A-06) | SaaS extern | No |
| Consultora externa (A-19) | La seva pròpia xarxa | No |
| Correu, passarel·la de pagament, correu transaccional | SaaS externs | No |
L'única cosa que travessa tots aquests contextos és la identitat. Si un atacant obté les credencials de la Lucía, no necessita travessar cap tallafoc: entra per la porta principal de cadascun d'aquests sistemes, des de qualsevol lloc del món, i tot el que faci semblarà legítim.
D'aquí la formulació que resumeix l'enfocament Zero Trust de 01-03 aplicat a aquest domini:
La identitat és el nou perímetre. No es confia en una petició per venir «de dins»; es confia per poder verificar qui la fa, amb quin dispositiu, per a quin recurs i sota quines condicions, a cada petició.
Tres conceptes que cal separar amb precisió, perquè es confonen constantment:
| Concepte | Pregunta | Exemple a Nimbus |
|---|---|---|
| Identificació | Qui dius que ets? | Introduir [email protected] |
| Autenticació | Ho pots demostrar? | Contrasenya + clau FIDO2 |
| Autorització | Què pots fer? | La Lucía pot desplegar; no pot veure nòmines |
I el parany recurrent: la majoria dels incidents greus no són fallades d'autenticació, són fallades d'autorització. L'IDOR de 01-01 passava amb una sessió perfectament vàlida. Autenticar bé i autoritzar malament és el patró número u de l'OWASP Top 10.
- Cicle de vida de la identitat i comptes orfes
Una identitat no és un registre estàtic: és un procés amb tres moments, i el tercer és el que gairebé sempre falla.
flowchart LR
A["ALTA\nQui entra, amb quins\naccessos i qui ho aprova"] --> B["CANVIS\nCanvi de rol, projecte,\npermisos temporals"]
B --> C["BAIXA\nRevocacio completa\na tots els sistemes"]
B -->|"si ningu no retira\nel que hi havia"| D["ACUMULACIO\nDE PRIVILEGIS"]
C -->|"si s oblida\nalgun sistema"| E["COMPTE\nORFE"]
D --> F["Un usuari amb\nmes permisos que\nqualsevol administrador"]
E --> G["Acces valid sense\npropietari ni vigilancia"]
2.1 Alta
Els quatre requisits d'una alta correcta:
- Basada en el rol, no en la còpia. L'error clàssic és «dona-li els mateixos permisos que a l'Iván». Es copien també els permisos que l'Iván va acumular per projectes antics, i el privilegi es propaga com una infecció. Ha d'existir un perfil d'accés per rol predefinit.
- Amb aprovació registrada del propietari de l'actiu (el camp «propietari» de l'inventari de 01-04 serveix exactament per a això).
- Amb caducitat si és temporal. Un becari, un contracte de tres mesos o una consultora han de tenir data de fi al sistema, no a la memòria d'algú.
- Amb identitat nominal. Res de comptes compartits: sense identitat nominal no hi ha traçabilitat, i sense traçabilitat no hi ha no-repudi (01-01).
2.2 Canvis: l'acumulació de privilegis
És el problema silenciós. El Rubén va entrar a suport, va ajudar sis mesos a facturació, va participar en la migració de dades i ara coordina l'equip. Si a cada canvi es van afegir permisos sense retirar els anteriors, el Rubén té avui més accessos que qualsevol altra persona de Nimbus, i ningú no ho ha decidit mai.
La regla: tot canvi de rol és una baixa del rol anterior i una alta del nou, no una suma. I tot permís temporal neix amb data de fi.
2.3 Baixa: el cas de Nimbus
Un desenvolupador marxa de Nimbus. La Lucía desactiva el seu compte de correu el mateix dia i dona el procés per acabat. Sis mesos després, en la revisió d'inventari, apareix això:
| Sistema | Estat després de la «baixa» | Risc |
|---|---|---|
| Correu corporatiu | Desactivat | — |
| Compte del proveïdor cloud (A-05) | Actiu, amb permisos de lectura sobre els buckets | Accés a dades de clients des de qualsevol lloc |
| GitHub | Actiu com a col·laborador extern del repositori | Accés al codi i a l'historial, inclosos secrets antics |
| VPN | Desactivada | — |
| Base de dades | Usuari personal dev_carlos actiu |
Accés directe a dades de producció |
| Clau SSH al servidor d'aplicacions | Present a authorized_keys |
Accés al servidor sense passar per cap autenticació centralitzada |
| Eina de tiquets (SaaS) | Activa | Dades de clients a les incidències |
| Grup de WhatsApp de suport | Present | Captures de pantalla amb dades de clients |
| Token personal de GitHub creat per ell | Actiu i sense caducitat | Accés al codi encara que es desactivi el compte |
Vuit accessos vius d'una persona que ja no treballa aquí. I cap d'ells no apareix en un tallafoc, perquè tots són legítims des del punt de vista tècnic.
Per què els comptes orfes són especialment perillosos, més enllà de l'obvi:
- Ningú no els vigila. Un ús anòmal del compte de la Lucía es pot notar; ningú no troba a faltar
dev_carlos. - Ningú no els manté. La seva contrasenya no es rota, no se'ls afegeix MFA quan es desplega, no se'n revisen els permisos.
- Són l'objectiu preferit del credential stuffing. Si aquella persona reutilitzava la contrasenya i apareix en una bretxa d'un altre servei, la porta continua oberta.
- No sempre hi ha mala intenció. L'exempleat que continua accedint «per ajudar» crea un problema igual de seriós: accés sense autorització vigent i sense control.
El procediment de baixa correcte per a Nimbus:
LLISTA DE COMPROVACIO DE BAIXA — s executa l ULTIM DIA, no despres
[ ] Desactivar la identitat central (SSO) — bloqueja tot el federat
[ ] Revocar sessions i tokens actius (no n hi ha prou de desactivar el compte:
una sessio viva continua funcionant)
[ ] Revisar la llista de sistemes NO federats, un a un:
[ ] Proveidor cloud [ ] GitHub i tokens personals
[ ] Base de dades [ ] Claus SSH a authorized_keys
[ ] SaaS de tiquets [ ] Passarela de pagament [ ] Correu transaccional
[ ] Retirar de grups i canals de missatgeria
[ ] Reassignar la propietat dels seus actius de l inventari (01-04)
[ ] Rotar els secrets compartits que coneixia
[ ] Recuperar el portatil i la clau de recuperacio de xifratge
[ ] Registrar la baixa completada, amb data i responsableEls dos punts que més s'obliden són la revocació de sessions i tokens —desactivar un compte no invalida una sessió ja emesa, com vas veure al pass-the-cookie de 02-03— i la rotació de secrets compartits: si aquella persona coneixia la contrasenya del NAS o la clau de la passarel·la, aquestes credencials estan compromeses per definició.
La mesura estructural que redueix el problema a la meitat: com més sistemes estiguin federats amb un únic proveïdor d'identitat, menys caselles cal marcar. És la raó principal per la qual l'SSO és una mesura de seguretat i no només de comoditat.
- Factors d'autenticació i les seves debilitats
Un factor és una categoria de prova. L'autenticació multifactor exigeix factors de categories diferents; dues contrasenyes no són dos factors.
| Factor | Què és | Exemples | Debilitats |
|---|---|---|---|
| Alguna cosa que saps | Coneixement | Contrasenya, PIN, resposta a pregunta | Es pot robar, endevinar, reutilitzar, fer-ne phishing i compartir sense deixar rastre |
| Alguna cosa que tens | Possessió | Clau FIDO2, app d'autenticació, targeta, telèfon | Es perd, es roba; l'SMS és interceptable; els codis són phishejables |
| Alguna cosa que ets | Biometria | Empremta, rostre | No es pot canviar si es compromet; falsos positius i negatius; a la pràctica desbloqueja un dispositiu, no autentica al servei |
| (Contextual) | On i com | IP, país, dispositiu conegut, hora | No és un factor per si sol: és un senyal de risc que modula l'exigència |
Tres precisions que corregeixen malentesos freqüents:
- La biometria gairebé mai no viatja al servidor. Quan l'Iván desbloqueja el mòbil amb l'empremta, la dada biomètrica no surt del dispositiu: desbloqueja una clau privada emmagatzemada al maquinari segur. El que el servei rep és una prova criptogràfica. Aquesta distinció és la que fa que les passkeys siguin sòlides i no un problema de privacitat.
- Les preguntes de seguretat no són un factor, són una contrasenya pitjor: la resposta sol ser pública o endevinable, i no es pot canviar el nom de l'escola de la teva infància.
- El context no substitueix un factor, però és enormement valuós combinat: exigir reautenticació quan canvia el país o el dispositiu és el que converteix un token robat en un token inútil.
- Contrasenyes: què diuen avui les guies
Les recomanacions van canviar substancialment en l'última dècada, i moltes organitzacions continuen aplicant les del 2005 —que empitjoren la seguretat—. La referència actual és la guia NIST SP 800-63B.
| Pràctica tradicional | Recomanació actual | Per què va canviar |
|---|---|---|
| Mínim 8 caràcters amb majúscules, minúscules, números i símbols | Mínim 12-15 caràcters, sense exigir composició | La complexitat obligatòria produeix Empresa2026! de forma predictible; la longitud aporta molta més resistència |
| Caducitat cada 90 dies | Sense caducitat forçada, tret d'indici de compromís | La rotació obligatòria genera variacions trivials (…2026! → …2027!) i augmenta l'ús de notes escrites |
| Prohibir enganxar al camp | Permetre enganxar sempre | Prohibir-ho impedeix l'ús de gestors de contrasenyes, que és el que de debò ajuda |
| Preguntes de seguretat com a suport | Eliminar-les | Respostes públiques o endevinables |
| Pistes de contrasenya | Eliminar-les | Són una filtració parcial de la contrasenya |
| — | Comprovar contra llistes de contrasenyes filtrades | Impedeix d'arrel el credential stuffing i el diccionari |
| — | Permetre tots els caràcters, inclosos espais i unicode | Facilita frases de pas llargues |
| — | Longitud màxima ≥ 64 caràcters | Un màxim baix delata que la contrasenya podria no emmagatzemar-se correctament |
La comprovació contra llistes de filtrades és la mesura individual més eficaç d'aquesta taula, i la menys implantada. Impedeix que algú triï una contrasenya que ja és als diccionaris que fan servir els atacants. S'implementa sense enviar la contrasenya enlloc: s'envien els primers caràcters del hash i es compara localment la resta (model de k-anonimat).
Gestors de contrasenyes. Per a Nimbus són obligatoris, i l'argument és simple: fan possible complir l'única cosa que de debò importa —una contrasenya llarga, aleatòria i diferent per servei— sense memoritzar res. L'objecció habitual («i si comprometen el gestor?») ignora l'alternativa real, que no és memoritzar 40 contrasenyes fortes: és reutilitzar-ne tres de febles. Un gestor amb contrasenya mestra forta i MFA és, amb una diferència enorme, l'opció més segura. Avantatge afegit i poc conegut: el gestor no omple les credencials en un domini que no coincideix, cosa que el converteix en un detector de phishing passiu.
L'emmagatzematge segur de contrasenyes al servidor —per què mai no es guarden en clar ni amb un hash simple, i què són bcrypt, scrypt i Argon2 amb el seu factor de cost i la seva sal— s'estudia en detall a 03-04. Aquí n'hi ha prou amb la regla: Nimbus mai no emmagatzema contrasenyes recuperables, i cap procés legítim no pot retornar a un client la seva contrasenya actual.
- MFA ordenat per resistència al phishing
L'MFA és la mesura més rendible de la lliçó, però no totes les formes d'MFA valen el mateix. La diferència decisiva és si el segon factor pot ser capturat i reutilitzat per un atacant interposat.
| Mètode | Com funciona | Resistència al phishing | Altres debilitats | Ús a Nimbus |
|---|---|---|---|---|
| SMS | Codi de 6 dígits per missatge | Molt baixa: el codi es teclegea i es pot reenviar en temps real | Intercanvi de SIM, interceptació, dependència de cobertura | Només com a últim recurs; millor que res |
| Correu electrònic | Codi o enllaç a la bústia | Molt baixa | Si el correu cau, cau tot | Evitar |
| TOTP (app d'autenticació) | Codi de 6 dígits derivat d'un secret compartit i del temps | Baixa-mitjana: continua sent un codi que es pot demanar | El secret es pot copiar en configurar-lo; finestra de 30 s | Mínim acceptable per a comptes normals |
| Push simple | Notificació amb «Aprovar / Denegar» | Baixa: vulnerable a MFA fatigue (02-03) | Aprovació sense context | Insuficient per si sol |
| Push amb number matching | Mostra un número a la pantalla que cal introduir a l'app | Mitjana: exigeix mirar el dispositiu original | Encara phishejable amb esforç | Acceptable |
| FIDO2 / WebAuthn i passkeys | Criptografia de clau pública vinculada al domini | Alta: resistent al phishing per disseny | Cost de la clau física; gestió de còpia de seguretat | Objectiu per a tots els comptes administratius |
| Certificat de client | Certificat al dispositiu | Alta | Gestió de PKI (03-06) | Per a servei a servei |
5.1 Per què FIDO2 és qualitativament diferent
Tots els mètodes anteriors comparteixen un defecte de fons: produeixen un secret que l'usuari transmet. Si l'usuari és en un web fals, transmet el secret a l'atacant, que el reenvia al lloc real en segons. Això és exactament el que fan els kits de phishing amb proxy invers, i per això «tenim MFA» va deixar de ser una resposta suficient.
FIDO2 trenca aquest esquema amb dues propietats:
- La clau privada no surt mai del dispositiu. No hi ha cap secret que l'usuari pugui lliurar, ni tan sols volent.
- La signatura està vinculada al domini (origin binding). El navegador inclou a l'operació l'origen real de la pàgina. Si l'usuari és a
nimbusreservas.example.portal-facturas.example, la clau produeix una signatura per a aquest domini, que el servidor legítim rebutja. L'atac no falla perquè l'usuari se n'adoni: falla perquè és criptogràficament invàlid.
Formulat d'una altra manera: amb TOTP o SMS, la defensa depèn que l'usuari detecti el web fals. Amb FIDO2, la defensa funciona encara que l'usuari no detecti res. És la diferència entre una mesura que depèn del criteri humà i una que no. Recorda el principi de 02-03: si la teva defensa exigeix que 38 persones encertin sempre, no tens defensa.
Les passkeys són FIDO2 amb la clau sincronitzada entre els dispositius de l'usuari a través del seu ecosistema, cosa que elimina la fricció principal (perdre la clau) a canvi de confiar en el sincronitzador.
5.2 Verificació de TOTP, explicada
Encara que l'objectiu sigui FIDO2, TOTP continua sent el mínim realista per a molts comptes. Convé entendre com funciona per dins.
import hmac, hashlib, struct, time
def codi_totp(secret: bytes, moment: int | None = None,
pas: int = 30, digits: int = 6) -> str:
"""Genera el codi TOTP de 6 digits (RFC 6238)."""
# 1. El "comptador" es el nombre d intervals de 30 s des de 1970.
# Emissor i verificador el calculen per separat: no es transmet res.
comptador = int((moment or time.time()) // pas)
# 2. HMAC-SHA1 del comptador amb el secret compartit.
# HMAC garanteix que nomes qui coneix el secret genera el valor (03-04).
resum = hmac.new(secret, struct.pack(">Q", comptador), hashlib.sha1).digest()
# 3. "Truncament dinamic": els 4 ultims bits indiquen des d on llegir.
desplacament = resum[-1] & 0x0F
valor = struct.unpack(">I", resum[desplacament:desplacament + 4])[0]
valor &= 0x7FFFFFFF # es descarta el bit de signe
# 4. Es redueixen a 6 digits.
return str(valor % (10 ** digits)).zfill(digits)
def verificar_totp(secret: bytes, codi: str, usuari_id: int,
finestra: int = 1) -> bool:
"""Verificacio correcta: tolerancia de rellotge, temps constant i
proteccio contra reutilitzacio."""
ara = time.time()
# (a) S accepten l interval actual i un abans/despres: els rellotges
# no estan perfectament sincronitzats. Finestres majors debiliten.
for delta in range(-finestra, finestra + 1):
esperat = codi_totp(secret, ara + delta * 30)
# (b) Comparacio en temps constant: evita deduir el codi
# mesurant quant triga la comparacio a fallar.
if hmac.compare_digest(esperat, codi):
# (c) Un codi nomes pot fer-se servir UNA vegada: sense aixo, un
# codi capturat serveix durant els 30-90 s seguents.
if cache.get(f"totp:{usuari_id}:{codi}"):
return False
cache.set(f"totp:{usuari_id}:{codi}", "1", expira=120)
return True
return FalseLes quatre decisions que separen aquesta implementació d'una d'ingènua:
- (a) Finestra de tolerància acotada. Sense tolerància, els usuaris amb el rellotge lleugerament desviat no poden entrar. Amb una finestra gran (per exemple ±5), el codi val gairebé cinc minuts i l'atac en temps real es torna trivial. ±1 és l'equilibri habitual.
- (b)
hmac.compare_digest. Una comparació normal de cadenes acaba així que troba una diferència, i aquest temps és mesurable. En temps constant no es filtra res. - (c) Consum d'un sol ús. És el control que més s'oblida: sense ell, un codi capturat per phishing continua sent vàlid durant la finestra restant, que és justament el que necessita l'atacant interposat.
- El secret s'emmagatzema xifrat a la base de dades, no en clar: qui el llegeixi pot generar codis indefinidament.
I la conclusió honesta: per ben implementat que estigui, TOTP continua sent phishejable, perquè l'usuari teclegea un codi en una pàgina. Per això la línia de treball per a Nimbus és TOTP com a mínim general i FIDO2 per a l'administratiu.
5.3 MFA fatigue i la seva correcció
Connectant amb 02-03: el bombardeig de notificacions explota que aprovar és un botó sense context. Les correccions, per ordre d'eficàcia:
- Number matching: obliga a mirar la pantalla de l'origen i a introduir un número, cosa que fa impossible aprovar per inèrcia.
- Context a la notificació: aplicació, ubicació aproximada, dispositiu. Una petició des d'un altre país és evident.
- Límit d'intents: després de 3 peticions denegades o ignorades, es bloqueja el compte i s'alerta.
- Alerta a seguretat davant de ràfegues: una ràfega de peticions MFA significa que algú ja té la contrasenya, amb independència que l'aprovi o no. És un senyal d'alt valor que gairebé ningú no vigila.
- SSO, federació i protocols d'identitat
6.1 Per què l'SSO és una mesura de seguretat
Amb inici de sessió únic, l'usuari s'autentica un cop davant d'un proveïdor d'identitat i accedeix a totes les aplicacions federades.
| Avantatge | Per què importa a Nimbus |
|---|---|
| Una baixa ho desactiva tot | Redueix dràsticament la llista de caselles de l'apartat 2.3 |
| MFA un cop, aplicat a tot arreu | No cal configurar-lo servei per servei |
| Un únic punt de registre | Tots els inicis de sessió en un registre, correlacionables |
| Polítiques centralitzades | Exigir dispositiu conegut o reautenticació per risc, en un sol lloc |
| Menys contrasenyes | Menys reutilització, menys phishing eficaç |
El contrapès, que cal assumir conscientment: el proveïdor d'identitat es converteix en l'actiu més crític de tots. Si cau, ningú no entra enlloc; si el comprometen, es compromet tot. Per això: MFA FIDO2 obligatori per als seus administradors, registre exhaustiu, i un procediment d'accés d'emergència amb credencials guardades fora de línia per al cas d'indisponibilitat.
6.2 OAuth 2.0 davant d'OpenID Connect: la diferència exacta
És la confusió més estesa del domini, i produeix sistemes insegurs.
| OAuth 2.0 | OpenID Connect (OIDC) | |
|---|---|---|
| Per a què serveix | Autorització: donar a una aplicació accés limitat a recursos en nom de l'usuari | Autenticació: acreditar qui és l'usuari |
| Pregunta que respon | «Pot aquesta app llegir el teu calendari?» | «Qui ets?» |
| Què lliura | Access token: una clau amb permisos | ID token (un JWT signat) amb la identitat |
| Qui el consumeix | L'API que rep el token | L'aplicació que fa el login |
| Relació | És la base | És una capa a sobre d'OAuth 2.0 |
La frase que cal fixar: OAuth 2.0 no és un protocol d'autenticació. Fer servir un token d'accés com a prova d'identitat és un error clàssic: aquest token diu què es pot fer, no qui ets, i podria haver estat emès per a una altra aplicació. Si el que necessites és «qui és aquest usuari», fes servir OIDC i valida l'ID token.
SAML és el veterà: mateix objectiu que OIDC (autenticació federada), basat en XML i asseveracions signades, molt implantat a l'entorn corporatiu. Per a integracions noves es prefereix OIDC per la seva simplicitat i el seu encaix natural amb aplicacions mòbils i SPA; SAML continua sent obligat quan un client corporatiu gran ho exigeix, i és una petició habitual en un SaaS B2B com Nimbus.
6.3 El flux correcte per a la SPA de Nimbus: Authorization Code + PKCE
sequenceDiagram
participant U as Usuari
participant SPA as SPA de Nimbus
participant IDP as Proveidor d identitat
participant API as API de Nimbus
SPA->>SPA: Genera code_verifier aleatori<br/>i code_challenge = SHA256(verifier)
SPA->>U: Redirigeix a l IdP amb code_challenge
U->>IDP: S autentica (contrasenya + FIDO2)
IDP->>IDP: Valida credencials i consentiment
IDP->>SPA: Redirigeix amb un CODI d un sol us
SPA->>IDP: Bescanvia codi + code_verifier
IDP->>IDP: Comprova SHA256(verifier) == challenge
IDP->>SPA: ID token (qui ets) + Access token (que pots)
SPA->>API: GET /api/v1/reservas<br/>Authorization: Bearer <access token>
API->>API: Valida signatura, iss, aud, exp i scope
API->>SPA: 200 amb les dades del tenant de l usuari
Per què PKCE és imprescindible i no un extra. El codi d'autorització viatja pel navegador, i en una aplicació pública (SPA o app mòbil) no hi ha manera de guardar un secret de client: qualsevol pot descompilar l'app o llegir el JavaScript. Si un atacant intercepta el codi, podria bescanviar-lo per tokens.
PKCE ho impedeix amb una idea senzilla: l'aplicació genera un valor aleatori (code_verifier), envia només el seu hash en iniciar el flux i presenta el valor original en bescanviar. Qui no tingui el code_verifier no pot fer servir el codi robat, i aquest valor no surt mai del navegador que va iniciar el flux.
Els quatre errors freqüents en aquest flux:
- Fer servir el flux implícit (tokens a l'URL): obsolet i desaconsellat.
- Guardar els tokens a
localStorage: accessible des de JavaScript, per tant robable amb un XSS. És preferible una galetaHttpOnlygestionada per un intermediari de servidor. - No validar
auda l'API: acceptar un token emès per a una altra aplicació. - No validar el paràmetre
state: és la protecció contra CSRF del mateix flux d'autenticació.
- Sessions i tokens: galetes i JWT
7.1 Galetes segures
Set-Cookie: sessio=aB3xK9...; HttpOnly; Secure; SameSite=Lax;
Path=/; Max-Age=3600; Domain=app.nimbusreservas.example| Atribut | Què fa | Quin atac atura |
|---|---|---|
HttpOnly |
El JavaScript no la pot llegir | Robatori de sessió mitjançant XSS |
Secure |
Només s'envia per HTTPS | Captura en trànsit |
SameSite=Lax |
No s'envia en peticions originades per un altre lloc | CSRF (02-02) |
Max-Age=3600 |
Vida curta amb renovació | Pass-the-cookie: redueix la finestra de la galeta robada |
Path i Domain acotats |
Abast mínim | Que la galeta viatgi a subdominis innecessaris |
Prefix __Host- al nom |
Força Secure, Path=/ i absència de Domain |
Fixació de galeta des d'un subdomini compromès |
SameSite=Strict davant de Lax: Strict és més segur però trenca la navegació des d'enllaços externs (l'usuari arriba al tauler i hi apareix com a no autenticat). Lax és l'equilibri habitual; per a operacions sensibles es combina amb token anti-CSRF.
7.2 JWT: estructura i què no ficar-hi dins
Un JWT té tres parts separades per punts: capçalera, càrrega útil i signatura, codificades en base64url.
eyJhbGciOiJSUzI1NiIsImtpZCI6IjIwMjYtMDMifQ.eyJpc3MiOiJodHRwczovL2lkc...
└──── capcalera ───┘ └──────── carrega util ───────┘ └─ signatura ─┘{
"iss": "https://identidad.nimbusreservas.example",
"sub": "usr_8812",
"aud": "https://api.nimbusreservas.example",
"exp": 1774000000,
"iat": 1773999100,
"jti": "9f2a4c1e-77bb-4a10-9c31-7d2e5a6b1c04",
"tenant_id": 42,
"roles": ["recepcio"]
}El primer que cal entendre, i el que més es malinterpreta: un JWT està signat, no xifrat. Qualsevol que el tingui pot llegir-ne el contingut sense cap clau. La signatura garanteix integritat i origen, no confidencialitat.
Què NO ha d'anar mai dins d'un JWT:
| No incloure | Per què |
|---|---|
| Contrasenyes o secrets | És text llegible per qualsevol |
| Dades personals sensibles (nom del pacient, servei mèdic) | Viatja a cada petició, es registra en registres i proxies, queda al navegador |
| Números de targeta o compte | Mateix motiu, amb obligacions específiques |
| Dades que canvien sovint | El token és una foto: si es revoca un rol, el token vell continua dient el contrari |
| Estructures grans | El JWT viatja a cada petició: penalitza el rendiment |
El que sí que ha de portar: un identificador de subjecte (sub), el tenant, els rols o àmbits mínims, i sempre iss, aud, exp, iat i jti.
7.3 Validació correcta davant de validació ingènua
# ============ VALIDACIO INGENUA I VULNERABLE ============
import jwt
def usuari_de_token_MALAMENT(token: str):
# (1) Sense verificar la signatura: qualsevol pot fabricar el token que vulgui
dades = jwt.decode(token, options={"verify_signature": False})
# (2) Sense comprovar caducitat, emissor ni audiencia
# (3) Es confia en els rols que venen a dins sense mes
return Usuari(id=dades["sub"], tenant=dades["tenant_id"],
roles=dades["roles"])Les tres fallades i per què cadascuna és explotable:
- Sense verificar la signatura, un atacant compon un JWT amb
"roles": ["admin"]i"tenant_id": 7i entra com a administrador de qualsevol clínica. La variant històrica d'aquesta fallada és acceptar l'algorismenoneque el mateix token declara a la seva capçalera: mai no s'ha de confiar en l'algdel token per decidir com validar-lo. - Sense comprovar
exp, un token robat fa un any continua servint. Senseissiaud, un token vàlid emès per un altre sistema o per a una altra aplicació s'accepta aquí. - Confiar cegament en els rols del token significa que un canvi de permisos o una baixa no tenen efecte fins que el token caduqui.
# ============ VALIDACIO CORRECTA ============
import jwt
from jwt import PyJWKClient
from fastapi import HTTPException
JWKS = PyJWKClient("https://identidad.nimbusreservas.example/.well-known/jwks.json")
EMISSOR = "https://identidad.nimbusreservas.example"
AUDIENCIA = "https://api.nimbusreservas.example"
def usuari_de_token(token: str):
try:
# (1) La clau publica s obte de l emissor segons el 'kid' de la
# capcalera. Permet ROTAR claus sense desplegar l API.
clau = JWKS.get_signing_key_from_jwt(token).key
dades = jwt.decode(
token,
clau,
algorithms=["RS256"], # (2) algorisme FIXAT per nosaltres,
# mai el que digui el token
issuer=EMISSOR, # (3) qui el va emetre
audience=AUDIENCIA, # (4) per a qui va ser emes
options={"require": ["exp", "iat", "iss", "aud", "sub", "jti"],
"verify_exp": True},
leeway=30, # (5) tolerancia de rellotge: 30 s
)
except jwt.ExpiredSignatureError:
raise HTTPException(401, "Sessio caducada")
except jwt.InvalidTokenError:
raise HTTPException(401, "Token no valid")
# (6) REVOCACIO: la signatura valida no basta. Es comprova que el token
# no estigui a la llista de revocats (baixa, tancament de sessio, robatori).
if cache.get(f"revocat:{dades['jti']}"):
raise HTTPException(401, "Sessio revocada")
# (7) Els permisos SENSIBLES es resolen contra la font de veritat,
# no contra el que digui el token emes fa 50 minuts.
permisos = carregar_permisos_vigents(dades["sub"], dades["tenant_id"])
return Usuari(id=dades["sub"], tenant=dades["tenant_id"],
roles=permisos, jti=dades["jti"])Les set decisions, explicades:
- Claus des de JWKS per
kid. Permet rotar la clau de signatura sense tocar l'API. Codificar la clau pública al codi converteix la rotació en un desplegament, i el que és difícil no es fa. algorithms=["RS256"]fixat. Tanca l'atac de confusió d'algorisme (per exemple, fer passar la clau pública RSA com a secret HMAC).- i 4.
issueriaudience. Senseaud, un token legítim per a un altre servei del mateix emissor seria acceptat per l'API de Nimbus. leeway. Evita fallades espúries per desfasament de rellotges; 30 s és raonable, minuts no.- Revocació per
jti. És el punt feble estructural del JWT: com que és autocontingut, és vàlid fins que caduca encara que l'usuari hagi estat donat de baixa. La llista de revocats amb vida igual a la del token resol el problema amb un cost mínim. - Permisos vigents des de la font de veritat. Els rols del token serveixen com a pista ràpida; les decisions sensibles es prenen amb dades actuals.
7.4 Vida dels tokens i refresh
| Token | Vida recomanada | On es guarda | Notes |
|---|---|---|---|
| Access token | 5-15 minuts | Memòria de l'aplicació | Curt perquè és difícil de revocar |
| Refresh token | Hores o dies, amb rotació | Galeta HttpOnly Secure SameSite |
Cada ús n'emet un de nou i invalida l'anterior |
| Token de servei (API) | Màxim 90 dies, rotació automatitzada | Gestor de secrets | Mai sense caducitat |
Rotació de refresh amb detecció de reutilització, que és la peça elegant: si un refresh token ja usat es presenta un altre cop, significa que algú en té una còpia robada. La resposta correcta és invalidar tota la família de tokens d'aquella sessió i forçar la reautenticació. És un mecanisme de detecció de robatori que funciona sense més infraestructura.
7.5 El token sense caducitat de 01-04, corregit
Recordem la troballa: el compte de servei nimbus-deploy-bot amb un token personal sense caducitat i permisos d'escriptura a tots els repositoris. Va ser a més la primera baula de l'atac encadenat de 02-02.
Tot el que hi ha de malament:
| Problema | Conseqüència |
|---|---|
| Sense caducitat | Robat un cop, serveix per sempre |
| Àmbit total sobre tots els repositoris | El compromís no està acotat |
| Lligat a un compte personal de tipus bot | No hi ha propietari clar ni traçabilitat |
| Emmagatzemat a la configuració del CI i probablement en portàtils | Múltiples còpies, superfície multiplicada |
| Sense registre d'ús revisat | Ningú no notaria un ús anòmal |
| Sense rotació | Mai no ha canviat des de la seva creació |
La correcció, per ordre de preferència:
- Eliminar-lo i fer servir credencials efímeres del propi CI (identitat federada de curta durada emesa per a cada execució). El millor secret és el que no existeix.
- Si no és possible: token d'aplicació amb àmbit per repositori, caducitat de 90 dies, rotació automatitzada i emmagatzematge al gestor de secrets.
- En qualsevol cas: alerta davant de qualsevol ús des d'una IP no esperada i revisió trimestral de tokens vius.
-- Consulta d higiene: tokens sense caducitat o sense us recent.
-- Hauria d executar-se mensualment i retornar SEMPRE zero files.
SELECT id, nom, propietari, creat_el, ultim_us, ambit
FROM tokens_api
WHERE caduca_el IS NULL -- mai no caduquen
OR caduca_el > now() + interval '90 days' -- vida excessiva
OR ultim_us < now() - interval '30 days' -- creats i oblidats
ORDER BY creat_el;Els tokens sense ús en 30 dies són tan perillosos com els eterns, i per la mateixa raó que els comptes orfes: ningú no els trobaria a faltar si algú comencés a fer-los servir.
- Models d'autorització i l'RBAC de Nimbus
Autenticar respon «qui ets». Autoritzar respon «què pots fer», i és on passen la majoria dels incidents greus.
| Model | Com decideix | Avantatge | Inconvenient | Exemple |
|---|---|---|---|---|
| DAC (discrecional) | El propietari del recurs decideix qui hi accedeix | Flexible, intuïtiu | Ingovernable a escala; origen dels enllaços «qualsevol amb l'enllaç» | El full de nòmines compartit per la Sara (01-04) |
| MAC (obligatori) | Una política central i immutable, per etiquetes de classificació | Molt estricte | Rígid i car | Entorns militars; SELinux |
| RBAC (per rols) | Permisos associats a rols, rols assignats a persones | Simple, auditable, escalable | No expressa condicions (hora, ubicació, propietat) | El model de Nimbus |
| ABAC (per atributs) | Regles sobre atributs d'usuari, recurs i context | Molt expressiu i granular | Difícil de raonar i d'auditar | «Només des d'Espanya, en horari laboral, si el recurs és del seu tenant» |
| ReBAC (per relacions) | Segons la relació entre subjecte i objecte en un graf | Natural per a compartició i jerarquies | Requereix infraestructura específica | «Ho pot veure perquè és el professional assignat a aquella cita» |
Què li convé a Nimbus: RBAC com a base, amb atributs per a les condicions. És el model híbrid que fa servir gairebé tothom: els rols defineixen el gruix dels permisos i unes poques regles contextuals cobreixen el que els rols no expressen (el tenant, l'horari, el dispositiu).
8.1 El disseny RBAC de Nimbus
Rols de client (dins d'una clínica o gimnàs):
| Rol | Pot | No pot |
|---|---|---|
| Administrador de centre | Gestionar usuaris del seu centre, veure tota l'agenda, configurar serveis, veure facturació | Sortir del seu tenant; accedir a dades d'un altre centre |
| Recepció | Crear, modificar i cancel·lar cites; consultar dades de contacte | Veure notes clíniques; exportar en massa; gestionar usuaris |
| Professional | Veure la seva agenda i les notes dels seus pacients | Veure l'agenda d'altres professionals; gestionar usuaris; facturació |
Rols interns de Nimbus:
| Rol | Pot | No pot |
|---|---|---|
| Suport (Rubén) | Veure metadades d'incidències; suplantar un usuari només amb justificació i aprovació, amb registre | Accedir a dades de clients sense tiquet; exportar; veure notes clíniques |
| Desenvolupament (Iván) | Accés total a preproducció amb dades pseudonimitzades | Accés a producció sense aprovació explícita i temporal |
| Sistemes (Lucía) | Administrar infraestructura; accés a producció amb MFA i registre | Llegir dades de negoci de forma rutinària; esborrar registres d'auditoria |
| Administració (Sara) | Facturació i RH | Accés a dades de clients finals |
# roles.yaml — definicio declarativa de la matriu de permisos.
# Avantatge de tenir-lo al repositori: es revisa com a codi,
# es versiona i el seu canvi deixa rastre a l historial.
roles:
centre_admin:
hereta: [recepcio]
permisos:
- usuaris:llegir
- usuaris:crear
- usuaris:desactivar
- facturacio:llegir
- configuracio:escriure
condicions:
tenant: propi # mai fora del seu tenant
recepcio:
permisos:
- cites:llegir
- cites:crear
- cites:modificar
- cites:cancellar
- clients:llegir_contacte # contacte SI, notes cliniques NO
condicions:
tenant: propi
exportacio_massiva: denegada
professional:
permisos:
- cites:llegir_propies
- notes_cliniques:llegir_propies
- notes_cliniques:escriure_propies
condicions:
tenant: propi
abast: agenda_assignada # atribut: nomes LES SEVES cites
nimbus_suport:
permisos:
- incidencies:llegir
- metadades_tenant:llegir
- suplantar_usuari # accio d alt risc
condicions:
suplantar_usuari:
requereix_tiquet: true
requereix_aprovacio: segon_operador
durada_maxima: 30m
registre: obligatori
notificar_al_client: true
nimbus_sistemes:
permisos:
- infraestructura:administrar
- desplegament:executar
condicions:
mfa: fido2
acces_produccio: just_in_time # es sollicita, caduca sol
registre_sessio: obligatori
denegat:
- auditoria:esborrar # NINGU no pot esborrar auditoriaQuatre decisions d'aquest disseny que mereixen atenció:
professionalfa servir un atribut (agenda_assignada), no un rol. «Els seus» pacients no es pot expressar només amb rols: és una relació entre l'usuari i el recurs. Aquí és on l'RBAC pur es queda curt i s'hi afegeix ABAC/ReBAC.suplantar_usuariés un permís explícit amb condicions, no una capacitat implícita del rol de suport. És la correcció del «veure com a client» que va permetre accedir a 40 clíniques a l'exercici de 02-02. I es notifica al client: la transparència és en si mateixa un control.auditoria:esborrarapareix com a denegat explícit per a tothom, inclòs sistemes. És la separació de funcions de 01-03: qui administra no pot esborrar la prova del que va fer, coherent amb l'INSERTsenseUPDATEdenimbus_api.- El fitxer és al repositori. Un canvi de permisos passa per revisió i queda a l'historial. Els permisos que es canvien a mà en una consola no deixen rastre comprensible.
I a la base de dades, la contrapartida del mateix disseny, coherent amb els rols de 01-03:
-- Rol de nomes lectura per al tauler de suport: mai no veu notes cliniques
CREATE ROLE nimbus_suport_rol NOLOGIN;
GRANT USAGE ON SCHEMA public TO nimbus_suport_rol;
-- Vista que EXCLOU les columnes sensibles: el minim privilegi
-- s implementa a l esquema, no en la confianca en el codi
CREATE VIEW v_incidencies_suport AS
SELECT r.id, r.tenant_id, r.data_hora, r.estat, c.nom AS client
FROM reserves r JOIN clients_finals c ON c.id = r.client_id;
-- deliberadament sense r.notes_cliniques ni c.dni
GRANT SELECT ON v_incidencies_suport TO nimbus_suport_rol;
REVOKE ALL ON reserves, clients_finals, nomines FROM nimbus_suport_rol;
- Multi-tenència i aïllament
Nimbus és multi-tenant: una única base de dades amb les dades de totes les clíniques. L'aïllament entre clients és el producte: si falla, no hi ha negoci.
Ja has treballat les dues peces —el filtre per tenant_id que va corregir l'IDOR a 01-01 i la seguretat a nivell de fila (RLS) que el fa obligatori des del motor a 01-03—. El que correspon aquí és com encaixa l'aïllament en el control d'accés:
| Capa | Què garanteix | Què passa si falla sola |
|---|---|---|
Token amb tenant_id |
L'API sap a quin tenant pertany la petició | Si es manipula, les capes següents l'aturen |
| Filtre a la consulta | Cada consulta es limita al tenant | Un oblit exposa dades alienes |
| RLS a PostgreSQL | El motor ho imposa encara que el codi ho oblidi | És la xarxa de seguretat |
| Rol de BD amb mínim privilegi | Acota el dany d'una injecció | Limita l'abast |
Auditoria amb tenant_id |
Permet detectar accessos creuats | És la capa detectiva |
La regla de disseny, en una frase: el tenant_id mai no procedeix d'un paràmetre que envia el client; procedeix sempre del token validat al servidor. Un endpoint que accepti ?tenant_id= del client és un IDOR amb un altre nom.
I la comprovació que convé automatitzar: una prova al CI que, amb el token d'un tenant, intenti accedir a un recurs d'un altre i exigeixi un 404. És una prova de cinc línies que hauria detectat l'IDOR original abans d'arribar a producció.
- Comptes privilegiats, de servei i de tercers
Els comptes amb permisos elevats són l'objectiu final de gairebé qualsevol atac. Mereixen un tractament diferent de la resta.
10.1 Separació del compte administratiu
El problema: si la Lucía navega, llegeix correu i administra la infraestructura amb el mateix compte, un phishing reeixit contra el seu correu lliura directament el control de tota la producció.
La solució: dues identitats per a la mateixa persona.
| Compte | Ús | Restriccions |
|---|---|---|
[email protected] |
Correu, documents, navegació, reunions | Sense permisos administratius |
[email protected] |
Només administració | Sense correu ni navegació; MFA FIDO2 obligatori; només des d'equip gestionat; sessió registrada; accés just-in-time |
El cost és una molèstia diària moderada; el benefici és que el vector més probable (el phishing al correu) deixa de conduir a l'actiu més crític.
10.2 PAM i accés just-in-time
PAM (gestió d'accessos privilegiats) agrupa les pràctiques de control dels comptes amb més poder:
| Pràctica | Què aporta | Aplicació a Nimbus |
|---|---|---|
| Caixa forta de credencials | Les contrasenyes administratives no les coneix ningú: se sol·liciten | Contrasenya de superusuari de PostgreSQL, compte arrel del proveïdor cloud |
| Accés just-in-time | El permís no existeix fins que se sol·licita, i caduca sol | Accés a producció concedit per 2 hores amb justificació |
| Gravació de sessió | Traçabilitat del que es va fer | Sessions del bastió |
| Aprovació d'un segon | Separació de funcions | Operacions destructives o accés a dades de clients |
| Rotació automàtica | Redueix la finestra d'una credencial filtrada | Credencials de servei i de base de dades |
El canvi mental que proposa el just-in-time: en lloc de preguntar «qui ha de tenir accés permanent?», es pregunta «per què algú tindria accés permanent?». En un sistema ben dissenyat, l'estat normal d'un compte administratiu és sense privilegis; els privilegis es concedeixen per minuts i desapareixen sols. És el principi de mínim privilegi de 01-03 portat a l'eix del temps: no només el mínim de permisos, també la mínima durada.
10.3 L'accés remot de la consultora (A-19)
És l'actiu amb criticitat crítica i propietària la Marta de l'inventari de 01-04, i el patró exacte de l'incident de Target que analitzarem a 02-06.
Situació actual:
| Aspecte | Estat | Risc |
|---|---|---|
| Comptes | Compartits per diversos tècnics | Sense traçabilitat: no se sap qui va fer què |
| Permisos | Administratius totals i permanents | Sense límit d'abast ni de temps |
| MFA | No | Una contrasenya filtrada dona accés ple |
| Registre | No revisat | Un ús indegut passaria inadvertit |
| Caducitat | Cap | L'accés sobreviurà al contracte |
| Contracte | Sense detall d'abast ni obligacions | Sense base per exigir res |
Configuració objectiu:
- Comptes nominals per tècnic de la consultora. Sense excepcions: sense nominació no hi ha no-repudi.
- MFA obligatori a tots els accessos.
- Accés just-in-time: sense privilegis de manera predeterminada; se sol·liciten amb motiu, els aprova la Lucía o la Marta i caduquen en hores.
- Abast acotat: només els sistemes que realment necessiten, mai «administrador de tot».
- Sessions registrades i revisades per mostreig.
- Caducitat automàtica de l'accés alineada amb el contracte, amb revisió trimestral.
- Alerta davant de qualsevol accés fora de la finestra pactada.
Nota: les obligacions contractuals amb el proveïdor —abast, confidencialitat, notificació d'incidents, subcontractació, auditoria— i la gestió del risc de tercers com a procés es tracten a 04-04, i les seves implicacions en matèria de protecció de dades a 06-03. S'han de validar amb assessoria jurídica.
10.4 Comptes de servei
Els comptes que fan servir els sistemes (no les persones) són el punt cec habitual: no tenen qui els reclami ni qui els revisi.
| Regla | Motiu |
|---|---|
| Un compte per servei, mai compartit entre diversos | Permet acotar el dany i saber qui va fer què |
| Propietari humà assignat | Algú ha de respondre'n a la recertificació |
Sense accés interactiu (NOLOGIN, sense shell) |
No ha de poder fer-se servir com a porta d'entrada humana |
| Permisos mínims i explícits | Com els rols nimbus_api i nimbus_informes de 01-03 |
| Credencials efímeres o rotades | Redueix la finestra d'una filtració |
| Ús registrat i amb línia base coneguda | Un servei té un patró molt predictible: qualsevol desviació és sospitosa |
- Recertificació: la revisió periòdica d'accessos
Tot l'anterior es degrada amb el temps. La recertificació és la revisió periòdica en què el propietari de cada actiu confirma —o retira— els accessos concedits.
Com es fa a Nimbus, de forma sostenible:
| Element | Decisió |
|---|---|
| Freqüència | Semestral per a accessos normals; trimestral per a privilegiats i tercers |
| Qui revisa | El propietari de l'actiu de l'inventari de 01-04, no l'equip tècnic |
| Què es revisa | Llista de persones amb accés, nivell, data de concessió i data d'últim ús |
| Regla per defecte | El que no es confirma explícitament, es retira. Si cal actuar per retirar, no es retira mai |
| Sortida | Llista signada i datada: és l'evidència que demanarà una auditoria (06-04) |
La dada que fa eficient la revisió és la data d'últim ús. Un permís sense ús en sis mesos gairebé mai no és necessari, i presentar la llista ordenada per aquest camp converteix una revisió tediosa en una decisió ràpida.
-- Insum per a la recertificacio: accessos sense us recent.
-- S envia al propietari de cada actiu perque confirmi o retiri.
SELECT u.email,
u.rol,
a.nom AS actiu,
p.concedit_el,
p.concedit_per,
MAX(l.ts) AS ultim_us,
CASE WHEN MAX(l.ts) IS NULL THEN 'MAI USAT'
WHEN MAX(l.ts) < now() - interval '180 days' THEN 'SENSE US 6 MESOS'
ELSE 'ACTIU' END AS estat
FROM permisos p
JOIN usuaris u ON u.id = p.usuari_id
JOIN actius a ON a.id = p.actiu_id
LEFT JOIN accessos_log l ON l.usuari_id = u.id AND l.actiu_id = a.id
GROUP BY u.email, u.rol, a.nom, p.concedit_el, p.concedit_per
HAVING MAX(l.ts) IS NULL OR MAX(l.ts) < now() - interval '180 days'
ORDER BY a.nom, ultim_us NULLS FIRST;Un permís «MAI USAT» és la millor notícia possible en una recertificació: es retira sense discussió, sense risc de trencar res i amb benefici immediat de reducció de superfície.
Errors Comuns i Consells
Errors comuns:
- Donar de baixa només el correu. És l'error de l'apartat 2.3: queden vius vuit accessos en sistemes no federats.
- Desactivar el compte sense revocar sessions ni tokens. Una sessió emesa continua funcionant; un token sense caducitat, per sempre.
- Copiar els permisos d'un altre empleat en donar d'alta. Propaga privilegis acumulats que ningú no va decidir concedir.
- Tractar tot l'MFA com a equivalent. SMS i FIDO2 es diferencien en si l'atac de phishing funciona o no.
- Forçar caducitat de contrasenyes cada 90 dies. Produeix variacions trivials i contrasenyes escrites en paper; les guies actuals ho desaconsellen tret d'indici de compromís.
- Fer servir OAuth 2.0 com si fos autenticació. Un token d'accés diu què es pot fer, no qui ets. Per a identitat, OIDC i validació de l'ID token.
- Guardar tokens a
localStorage. Un XSS els roba. GaletaHttpOnlyo intermediari de servidor. - Confiar en un JWT sense verificar signatura,
exp,issiaud, o deixar que el token triï l'algorisme. - Ficar dades personals dins del JWT. Està signat, no xifrat: qualsevol el llegeix.
- Acceptar
tenant_idcom a paràmetre del client. És un IDOR amb un altre nom. - Concedir accés privilegiat permanent perquè «és més còmode». L'estat normal d'un compte administratiu hauria de ser sense privilegis.
- Recertificar amb la regla «es retira el que algú demani retirar». Per defecte s'ha de retirar el que no es confirma.
Consells:
- Comença pel que més rendeix: FIDO2 als comptes administratius i caducitat a tots els tokens. Són dues tardes de feina i tanquen els dos vectors més explotats.
- Escriu la llista de comprovació de baixa de l'apartat 2.3 i executa-la l'últim dia, no després. Guarda'n constància signada.
- Separa el compte administratiu del personal per a la Lucía i la Marta. És incòmode una setmana i protegeix durant anys.
- Posa el fitxer
roles.yamlal repositori i tracta'l com a codi: revisió, historial i desplegament controlat. - Afegeix al CI la prova d'aïllament entre tenants. Cinc línies que vigilen permanentment la fallada més cara d'un SaaS.
- Executa avui la consulta de tokens sense caducitat. Si retorna files, ja tens la teva tasca de la setmana.
- A la recertificació, ordena sempre per data d'últim ús: converteix una revisió tediosa en decisions d'un segon.
Exercicis
Exercici 1 — Dissenyar la baixa d'un empleat i detectar el que falta
L'Iván deixa Nimbus. La Lucía executa el següent:
- Desactiva el seu compte de correu.
- El treu del canal intern de missatgeria.
- Recupera el portàtil.
- El retira del repositori de GitHub com a col·laborador.
Dos mesos després, en la revisió trimestral, es detecta activitat: algú ha executat un desplegament a les 4:10 de la matinada.
Es demana: (a) enumera almenys sis accessos que probablement continuïn vius i explica per què cadascun és perillós; (b) escriu la llista de comprovació de baixa completa que s'hauria d'haver aplicat; (c) explica com va poder executar-se un desplegament si el compte de GitHub estava retirat, amb dues hipòtesis tècniques diferents; (d) proposa tres mesures estructurals que redueixin aquest risc amb independència de la disciplina de qui executi la baixa.
Exercici 2 — Auditar una implementació de tokens
Aquest és el codi d'autenticació d'una nova versió de l'API de Nimbus:
import jwt, datetime
from fastapi import HTTPException
SECRET = "nimbus-2026"
def emetre(usuari):
carrega = {
"sub": usuari.email,
"nom": usuari.nom_complet,
"dni": usuari.dni,
"tenant_id": usuari.tenant_id,
"roles": usuari.roles,
"notes_internes": usuari.notes,
}
return jwt.encode(carrega, SECRET, algorithm="HS256")
def validar(token: str):
dades = jwt.decode(token, SECRET,
algorithms=["HS256", "none"],
options={"verify_exp": False})
return Usuari(id=dades["sub"], tenant=dades["tenant_id"],
roles=dades["roles"])
@app.post("/api/v1/sesion")
def login(email: str, password: str):
u = autenticar(email, password)
if not u:
raise HTTPException(401, "Usuari no trobat o clau incorrecta")
return {"token": emetre(u), "expira": "mai"}Es demana: (a) identifica tots els problemes de seguretat, classificant-los en crítics, alts i mitjans; (b) explica l'impacte concret dels dos més greus sobre les dades de les clíniques; (c) reescriu emissió i validació correctament; (d) explica què canvia si l'API es desplega en tres instàncies i cal revocar una sessió immediatament.
Exercici 3 — Autorització d'una funcionalitat nova
Nimbus llança els informes d'ocupació per professional. Requisits de negoci:
- L'administrador de centre veu l'informe de tots els professionals del seu centre.
- El professional veu només el seu propi informe.
- Recepció no accedeix a aquesta funcionalitat.
- Suport de Nimbus (Rubén) pot veure l'informe d'un centre només amb un tiquet obert i aprovació d'un segon operador, durant 30 minuts com a màxim, i el centre ha de ser notificat.
- Els informes es poden exportar a CSV, amb un màxim de 5.000 files i 6 peticions per hora i usuari.
- Un professional donat de baixa fa més de 90 dies no ha d'aparèixer.
Es demana: (a) decideix quin model o combinació de models d'autorització faries servir i justifica-ho; (b) escriu el fragment de roles.yaml corresponent; (c) escriu el codi Python de l'endpoint amb totes les comprovacions d'autorització, indicant en comentaris quin atac prevé cadascuna; (d) indica tres esdeveniments que registraries i una alerta que construiries sobre ells.
Solucions
Solució 1
(a) Accessos que probablement continuïn vius:
| Accés | Per què és perillós |
|---|---|
| Compte del proveïdor cloud (A-05) | Accés a buckets, base de dades i a la mateixa infraestructura; és l'actiu del qual depenen gairebé tots |
| Token personal de GitHub creat per l'Iván | Sobreviu a la retirada del compte: un token és una credencial independent |
Usuari personal a PostgreSQL (dev_ivan) |
Accés directe a dades de producció sense passar per l'API ni pels seus controls |
Clau SSH a authorized_keys dels servidors |
Accés als servidors sense autenticació centralitzada; invisible per a l'SSO |
| Secrets que coneixia (contrasenya de BD, claus de la passarel·la, credencial del correu transaccional) | Continuen sent vàlids: els coneix algú sense relació amb l'empresa |
| Eines SaaS no federades (tiquets, monitoratge, gestor d'errors) | Contenen dades de clients i traces de producció |
| Sessions actives en navegadors i app mòbil | Desactivar el compte no invalida una sessió emesa (pass-the-cookie, 02-03) |
| Còpies locals de dades al seu portàtil o en emmagatzematge personal | Ni tan sols requereixen accés: les dades ja van sortir |
(b) La llista de comprovació de l'apartat 2.3, executada l'últim dia, amb constància signada i amb reassignació de la propietat dels seus actius de l'inventari.
(c) Dues hipòtesis del desplegament nocturn:
- Token personal viu. L'Iván va crear un token de GitHub amb permisos d'escriptura i sense caducitat —el patró de la troballa de 01-04—. Retirar el compte com a col·laborador no invalida el token, que continua autenticant davant de l'API del servei. És la hipòtesi més probable.
- Clau SSH o credencial d'infraestructura. El desplegament no va passar per GitHub: es va executar directament contra el servidor amb una clau SSH que va quedar a
authorized_keys, o amb credencials del proveïdor cloud que coneixia.
En tots dos casos, la lliçó és la mateixa: la identitat no és només el compte. És el compte més tots els artefactes que ell va crear i que viuen de forma independent.
(d) Tres mesures estructurals:
- Federar tot el que sigui possible amb el proveïdor d'identitat, de manera que una única baixa desactivi el màxim nombre d'accessos. Redueix la dependència de la disciplina humana.
- Prohibir per política els tokens sense caducitat i les claus SSH personals als servidors, substituint-los per credencials efímeres emeses pel CI i accés per bastió amb identitat central. Un artefacte que no existeix no s'oblida.
- Recertificació trimestral amb la consulta de l'apartat 11, que detecta el que es va escapar del procés. És la xarxa de seguretat: assumeix que la baixa fallarà alguna vegada i limita quant de temps passa fins que es detecta.
Solució 2
(a) Problemes trobats:
| Gravetat | Problema | Detall |
|---|---|---|
| Crític | algorithms=["HS256", "none"] |
Acceptar none permet fabricar tokens sense signatura: qualsevol es converteix en administrador de qualsevol tenant |
| Crític | SECRET = "nimbus-2026" al codi |
Secret feble, endevinable i present al repositori; permet signar tokens arbitraris |
| Crític | verify_exp: False i "expira": "mai" |
Els tokens no caduquen mai: robar-ne un equival a accés permanent |
| Alt | Sense exp, iss, aud, iat ni jti a la càrrega |
Impossible acotar validesa, origen, destinatari ni revocar |
| Alt | dni, nom i notes_internes dins del JWT |
El JWT està signat però no xifrat: dades personals llegibles per qualsevol i presents en registres i navegador |
| Alt | Sense mecanisme de revocació | Una baixa o un robatori no tenen efecte |
| Mitjà | HS256 amb secret compartit | Qualsevol servei que validi pot també emetre; amb RS256 només l'emissor signa |
| Mitjà | sub és el correu |
Un canvi de correu trenca la identitat; millor un identificador estable |
| Mitjà | Missatge d'error que distingeix casos | «Usuari no trobat o clau incorrecta» és acceptable, però convé un missatge totalment uniforme i un temps de resposta constant per evitar l'enumeració d'usuaris (02-02) |
(b) Impacte dels dos més greus. Amb none acceptat, un atacant construeix un token amb "tenant_id": 7 i "roles": ["centre_admin"], sense signatura, i l'API l'accepta. Resultat: accés complet a l'agenda i les dades de qualsevol clínica, sense necessitat de credencials, sense phishing i sense explotar res més. Combinat amb l'absència de caducitat, un token obtingut una sola vegada —d'un registre, d'una captura, d'un portàtil— dona accés indefinit. En termes de la tríada CIA, això és una pèrdua total de confidencialitat i integritat sobre dades que revelen informació de salut, i desencadena obligacions de notificació (06-03).
(c) Codi correcte:
import jwt, uuid, datetime
from jwt import PyJWKClient
from fastapi import HTTPException
EMISSOR = "https://identidad.nimbusreservas.example"
AUDIENCIA = "https://api.nimbusreservas.example"
JWKS = PyJWKClient(f"{EMISSOR}/.well-known/jwks.json")
CLAU_PRIVADA = gestor_secrets.llegir("jwt/clau-privada") # mai al codi
def emetre(usuari):
ara = datetime.datetime.now(datetime.timezone.utc)
carrega = {
"iss": EMISSOR,
"aud": AUDIENCIA,
"sub": str(usuari.id), # identificador estable, no el correu
"tenant_id": usuari.tenant_id,
"roles": usuari.roles, # pista rapida; el sensible es recarrega
"iat": ara,
"exp": ara + datetime.timedelta(minutes=15), # vida curta
"jti": str(uuid.uuid4()), # identificador per revocar
# sense nom, sense dni, sense notes internes
}
return jwt.encode(carrega, CLAU_PRIVADA, algorithm="RS256",
headers={"kid": "2026-03"})
def validar(token: str):
try:
clau = JWKS.get_signing_key_from_jwt(token).key
dades = jwt.decode(token, clau,
algorithms=["RS256"], # fixat, mai del token
issuer=EMISSOR, audience=AUDIENCIA,
options={"require": ["exp", "iat", "iss",
"aud", "sub", "jti"]},
leeway=30)
except jwt.ExpiredSignatureError:
raise HTTPException(401, "Sessio caducada")
except jwt.InvalidTokenError:
raise HTTPException(401, "Token no valid")
if cache.get(f"revocat:{dades['jti']}"):
raise HTTPException(401, "Sessio revocada")
permisos = carregar_permisos_vigents(dades["sub"], dades["tenant_id"])
return Usuari(id=dades["sub"], tenant=dades["tenant_id"],
roles=permisos, jti=dades["jti"])(d) Revocació immediata amb tres instàncies. Un JWT és autocontingut: les tres instàncies el validen sense consultar ningú, així que una baixa no té efecte fins que caduca. Solucions, combinables:
- Llista de revocació compartida (Redis) consultada per
jti, amb vida igual a la del token: màxim 15 minuts d'entrades. Cost: una consulta ràpida per petició; és el que fa el codi anterior. - Vida curta de l'access token (5-15 min) amb refresh rotatori. La revocació real s'aplica al refresh, que sí que es comprova contra la base de dades: la finestra màxima d'exposició és la vida de l'access token.
- Marca d'invalidació per usuari (
tokens_valids_des_de): es rebutja tot token ambiatanterior a aquesta marca. Revoca totes les sessions d'una persona amb un sol camp, ideal per a una baixa o un compromís.
El compromís de fons: el JWT és ràpid i sense estat, però la revocació exigeix estat. La solució pràctica no és triar un extrem, sinó escurçar la vida del token perquè la finestra sigui acceptable i mantenir un mecanisme de revocació per a l'urgent.
Solució 3
(a) Model triat: RBAC com a base + ABAC/ReBAC per a l'abast i les condicions. Justificació:
- Els tres perfils (administrador de centre, professional, recepció) són rols: l'RBAC els expressa de forma directa i auditable.
- «Només el seu propi informe» no és un rol: és una relació entre l'usuari i el recurs (el professional assignat). Requereix ReBAC o un atribut.
- «Amb tiquet, aprovació, 30 minuts i notificació» són condicions contextuals: ABAC.
- «Professional donat de baixa fa més de 90 dies» és un atribut temporal del recurs.
L'RBAC pur obligaria a crear un rol per professional, cosa ingovernable. L'ABAC pur seria expressiu però molt difícil d'auditar. L'híbrid és el que fan servir els sistemes reals.
(b) Fragment de roles.yaml:
permisos_nous:
informes_ocupacio:llegir_centre:
roles: [centre_admin]
condicions:
tenant: propi
professionals_visibles: baixa_fa_menys_de_90_dies
informes_ocupacio:llegir_propi:
roles: [professional]
condicions:
tenant: propi
abast: professional_id == usuari.professional_id
informes_ocupacio:exportar:
roles: [centre_admin, professional]
limits:
files_maximes: 5000
peticions_per_hora: 6
registre: obligatori
informes_ocupacio:llegir_suport:
roles: [nimbus_suport]
condicions:
requereix_tiquet: true
requereix_aprovacio: segon_operador
durada_maxima: 30m
notificar_al_client: true
registre: obligatori
denegat_per: [recepcio](c) Endpoint amb les comprovacions:
@app.get("/api/v1/informes/ocupacion")
@limitador.limit("6/hour") # abus d API i DDoS aplicatiu (02-02)
def informe_ocupacio(professional_id: int | None = None,
format: str = "json",
usuari = Depends(usuari_actual)):
# 1. AUTORITZACIO PER ROL. Recepcio no hi arriba.
# Preve: escalada vertical de privilegis.
if not usuari.te_algun(["centre_admin", "professional",
"nimbus_suport"]):
raise HTTPException(403)
# 2. TENANT DES DEL TOKEN, MAI DEL CLIENT.
# Preve: IDOR entre tenants (01-01) i acces creuat.
tenant = usuari.tenant_id
# 3. ABAST SEGONS EL ROL (la part ABAC/ReBAC).
# Preve: escalada horitzontal entre professionals del mateix centre.
if usuari.te("professional"):
if professional_id not in (None, usuari.professional_id):
raise HTTPException(404) # 404, no 403: no revela existencia
professional_id = usuari.professional_id
# 4. SUPORT: condicions estrictes i tracabilitat.
# Preve: l abus de "veure com a client" de l exercici de 02-02.
if usuari.te("nimbus_suport"):
sessio = sessions_suport.vigent(usuari.id, tenant)
if not sessio or not sessio.aprovada_per_segon or sessio.caducada():
raise HTTPException(403, "Requereix tiquet aprovat i vigent")
tenant = sessio.tenant_id # el tenant el fixa l aprovacio
notificar_al_client(tenant, usuari.id, "acces_suport_informe")
# 5. CONSULTA ACOTADA: filtre de tenant, exclusio de baixes i LIMIT.
# Preve: exposicio de dades de mes i DDoS aplicatiu.
files = db.execute("""
SELECT p.id, p.nom, COUNT(r.id) AS cites,
SUM(CASE WHEN r.estat='completada' THEN 1 ELSE 0 END) AS completades
FROM professionals p
LEFT JOIN reserves r ON r.professional_id = p.id
AND r.tenant_id = :t
WHERE p.tenant_id = :t
AND (p.baixa_el IS NULL OR p.baixa_el > now() - interval '90 days')
AND (:pid::int IS NULL OR p.id = :pid)
GROUP BY p.id, p.nom
LIMIT 5000""",
{"t": tenant, "pid": professional_id}).fetchall()
# 6. AUDITORIA: qui, que, quant, per a qui.
registrar_auditoria(usuari_id=usuari.id, tenant_id=tenant,
accio="informe_ocupacio",
format=format, num_registres=len(files),
professional_id=professional_id)
if format == "csv":
return exportar_csv(files, maxim=5000)
return {"dades": [dict(f) for f in files]}(d) Tres esdeveniments a registrar i una alerta:
| Esdeveniment | Camps |
|---|---|
informe_ocupacio_consultat |
usuari_id, tenant_id, rol, professional_id, num_registres, format |
acces_suport_iniciat |
usuari_id, tenant_id, tiquet, aprovat_per, caduca_el |
exportacio_csv |
usuari_id, tenant_id, num_registres, ip |
Alerta: accés de suport a més de 3 tenants diferents en 24 hores, o qualsevol accés de suport sense tiquet associat. És exactament el senyal que hauria detectat en hores —en lloc de en tres dies— l'incident del tauler de suport de l'exercici de 02-02, i és barat de construir perquè els camps ja són al registre. Una segona alerta útil: qualsevol exportació CSV fora de l'horari laboral del tenant, que aprofita que Nimbus coneix l'horari de cada centre.
Conclusió
Has treballat el domini que més incidents decideix avui. Saps per què la identitat és el nou perímetre: cap dels actius rellevants de Nimbus no és darrere d'una frontera de xarxa, i l'única cosa que travessa tots els contextos és qui ets. Has separat amb precisió identificació, autenticació i autorització, i has fixat el parany que es repeteix a tot el curs: la majoria dels incidents greus no són fallades d'autenticació, sinó d'autorització. Has recorregut el cicle de vida de la identitat i el seu punt feble, la baixa, amb el cas concret dels vuit accessos que sobreviuen a una desactivació de correu, la llista de comprovació que ho evita i les dues coses que sempre s'obliden: revocar sessions i tokens, i rotar els secrets compartits.
En contrasenyes t'endús l'actualització de les guies —longitud sobre complexitat, sense caducitat forçada, permetre enganxar, i la mesura més eficaç i menys implantada: comprovar contra llistes de filtrades— i l'argument definitiu a favor dels gestors. En MFA has après la jerarquia que de debò importa, la de resistència al phishing, i la raó per la qual FIDO2 és qualitativament diferent: la clau no surt mai del dispositiu i la signatura està vinculada al domini, de manera que la defensa funciona encara que l'usuari no detecti res. Has vist per dins la verificació TOTP amb les seves quatre decisions —finestra acotada, comparació en temps constant, consum d'un sol ús i secret xifrat— i la correcció de l'MFA fatigue amb number matching.
En federació has fixat la diferència exacta entre OAuth 2.0, que autoritza, i OpenID Connect, que autentica, juntament amb el flux Authorization Code + PKCE i la raó per la qual PKCE és imprescindible en aplicacions públiques. En sessions i tokens has recorregut els atributs de galeta que neutralitzen XSS, CSRF i captura en trànsit, i has comparat una validació de JWT ingènua amb una de correcta: signatura verificada amb clau de l'emissor, algorisme fixat per nosaltres, iss, aud i exp comprovats, revocació per jti i permisos sensibles recarregats de la font de veritat. I has corregit el token sense caducitat de 01-04, la primera baula de l'atac encadenat de 02-02.
En autorització has comparat DAC, MAC, RBAC, ABAC i ReBAC i has construït el disseny real de Nimbus: rols de client i rols interns en un roles.yaml versionat, amb suplantar_usuari com a permís condicionat i notificat, i auditoria:esborrar denegat a tothom. Has tancat l'aïllament multi-tenant amb la seva regla d'or —el tenant_id ve sempre del token, mai del client— i la seva prova automatitzada al CI. I has acabat amb els comptes privilegiats: la separació del compte administratiu, PAM i l'accés just-in-time que converteix el mínim privilegi en mínima durada, la configuració objectiu per a l'accés de la consultora (A-19) i la recertificació periòdica la regla decisiva de la qual és que el que no es confirma, es retira.
Ja tens l'arsenal complet del mòdul: saps què s'ataca, com, amb què es defensa i com es controla l'accés. Falta la prova de realitat. A l'última lliçó del mòdul, Casos d'Estudi d'Incidents de Ciberseguretat (02-06), aplicarem un mètode d'anàlisi a incidents reals i públics —Target, WannaCry, Equifax, SolarWinds, Colonial Pipeline i una fuita per emmagatzematge al núvol mal configurat—, mapejarem cadascun als conceptes del curs, n'extraurem els patrons que es repeteixen sense excepció i reconstruirem hora a hora un incident fictici de ransomware a Nimbus que entra per l'accés remot de la consultora, per respondre a l'única pregunta que importa: què hauria canviat el desenllaç.
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
