Tanques el mòdul amb la lliçó integradora. Les sis anteriors et van donar les peces per separat —primitives, protocols i gestió de claus—; aquesta les posa totes alhora sobre el sistema real de Nimbus i respon a la pregunta que un professional ha de saber contestar davant de la seva direcció: quina dada està protegida, davant de quina amenaça, amb quina primitiva i on viu la clau. Veuràs el contrast decisiu entre xifratge en trànsit i en repòs, els tres nivells del xifratge en repòs amb el punt que gairebé ningú no té clar —que el xifratge de disc no protegeix davant d'una API compromesa—, el xifratge camp a camp amb el problema pràctic que introdueix, la signatura d'artefactes al CI/CD, les URL signades de 120 segons explicades per dins i la diferència real entre pseudonimitzar i anonimitzar.

Contingut

  1. Mapa criptogràfic de Nimbus de punta a punta
  2. En trànsit davant d'en repòs
  3. Els tres nivells del xifratge en repòs
  4. Xifratge a nivell de camp: les notes clíniques
  5. El problema que introdueix: ja no es pot cercar
  6. Xifratge de còpies de seguretat
  7. Signatura de codi i artefactes al CI/CD
  8. Tokens signats i URL signades per dins
  9. Pseudonimització i anonimització
  10. Casos que se solen fer malament
  11. Taula de referència: dada, amenaça, protecció i clau

  1. Mapa criptogràfic de Nimbus de punta a punta

flowchart TB
    U["Navegador i app movil\nPasskeys al modul segur\ndel dispositiu (02-05)"]
    U -->|"TLS 1.3: ECDHE + AES-GCM\nHSTS, forward secrecy"| API["API FastAPI\nValida el JWT signat (kid)\nAutoritza per tenant"]
    API -->|"TLS + credencials\ndel gestor de secrets"| DB["PostgreSQL\nXifratge de disc del proveidor\nArgon2id en credencials\nAES-GCM en notes cliniques"]
    API -->|"URL signades amb HMAC\ncaducitat 120 s"| S3["Bucket A-05\nAdjunts amb xifratge de sobre\nclau de dades per objecte"]
    API -->|"TLS + HMAC-SHA256\nals webhooks"| PG["Passarela de pagament"]
    API -->|"TLS + DKIM al sortint"| EM["Email transaccional"]
    DB --> BK["Copies 3-2-1-1-0\nXifrades i immutables.\nClau FORA de produccio"]
    S3 --> BK
    CI["CI/CD GitHub Actions\nSBOM, escaneig de secrets,\nartefactes SIGNATS"] -->|"desplegament verificat"| API
    KMS["KMS / gestor de secrets\nClau mestra que no surt.\nAuditoria per operacio"] -.->|"claus de dades"| API
    KMS -.-> BK

Nou trams, nou decisions criptogràfiques. Fixa't en un detall del diagrama: el KMS apareix amb línia discontínua perquè no és al camí de les dades, sinó al de les claus. Aquesta separació és justament el que fa que un compromís de l'API no lliuri la clau mestra (03-06).

I un advertiment que travessa tota la lliçó: en aquest mapa hi ha trams on la criptografia no protegeix res, i saber quins és tan important com saber xifrar. Un atacant que obté un token vàlid de l'API entra per la porta principal: TLS el protegeix, el xifratge de disc desxifra per ell i el KMS li serveix les claus de dades amb tota correcció. La criptografia no és la defensa contra aquell atac; ho són l'autorització, el límit de taxa i la detecció.


  1. En trànsit davant d'en repòs

En trànsit En repòs
Què protegeix Les dades mentre viatgen per una xarxa Les dades mentre estan emmagatzemades
Mecanisme TLS (03-05), SSH, VPN Xifratge simètric: disc, base de dades o camp
Amenaça que atura Escolta i interceptació a la xarxa Robatori del suport, còpia del volum, accés al fitxer
Durada Mil·lisegons Anys
A Nimbus Tota comunicació externa i interna PostgreSQL, bucket A-05, còpies, portàtils

El xifratge en trànsit el va cobrir per complet la lliçó 03-05, així que aquí només interessa el seu paper dins del conjunt: protegeix el tram i acaba a l'extrem. Quan la dada arriba al servidor de Nimbus, TLS ha complert i desapareix; a partir d'aquí, la protecció ha de venir d'una altra banda. La resta de la lliçó tracta precisament d'aquella altra banda.


  1. Els tres nivells del xifratge en repòs

Aquest és l'apartat que desfà més malentesos. «Xifrem en repòs» pot significar tres coses molt diferents, i cadascuna atura una amenaça diferent.

Disc complet (LUKS, BitLocker, xifratge del proveïdor) Nivell de base de dades (TDE, xifratge del volum gestionat) Nivell d'aplicació (camp a camp)
Qui xifra El sistema operatiu o el proveïdor El motor de base de dades El teu codi, abans de desar
Qui té la clau El sistema, en arrencar El motor Només l'aplicació, via KMS
Atura Robatori del portàtil o del disc; retirada de maquinari; accés a l'emmagatzematge físic A més: còpia del fitxer de dades o del volum; accés al magatzem de còpies del motor A més: un administrador de base de dades, un bolcat, una injecció SQL o una còpia restaurada en un altre lloc
NO atura Res del que passi amb el sistema encès Res que arribi a través del motor: un SELECT legítim retorna text en clar Un atacant que controli l'aplicació
Cost Nul, transparent Baix, transparent Alt: canvia el model de dades i trenca cerques
Ús Sempre. És higiene bàsica Sempre que el proveïdor ho ofereixi Només per a les dades més sensibles

El punt clau, dit sense embuts: el xifratge de disc protegeix davant d'un lladre que s'emporta el maquinari. No protegeix absolutament res davant d'una API compromesa, una injecció SQL o un IDOR, perquè en tots aquests casos el sistema està encès i el motor desxifra encantat per a qui pregunta. És exactament el que va passar a la fuita per bucket mal configurat de 02-06: les dades estaven xifrades en repòs i van sortir igual, perquè l'accés era «legítim».

D'aquí la regla:

Xifratge de disc i de base de dades: sempre, perquè són gratis. Però no els comptis com a defensa davant dels atacs que realment amenacen Nimbus. Per a això cal xifratge a nivell d'aplicació —per al més sensible— i, sobretot, control d'accés (02-05).


  1. Xifratge a nivell de camp: les notes clíniques

Nimbus desa, al costat de cada cita, un camp de notes que a les clíniques de fisioteràpia pot contenir informació que revela l'estat de salut del pacient. És la dada més sensible del sistema i el candidat natural al xifratge a nivell d'aplicació.

Nota de validació professional. El tractament de dades que revelen informació de salut està subjecte a exigències reforçades. El disseny tècnic que segueix és necessari però no suficient: cal documentar la base jurídica, la minimització, els terminis de conservació i l'anàlisi de riscos, i validar-ho amb el responsable de protecció de dades i amb assessoria jurídica. El marc normatiu es tracta a 06-03.

ALTER TABLE cites
    ADD COLUMN notes_xifrades   BYTEA,       -- versio || nonce || ct+etiqueta
    ADD COLUMN notes_clau_dk    BYTEA,       -- clau de dades XIFRADA pel KMS
    ADD COLUMN notes_kms_kid    TEXT,        -- quina clau mestra la va xifrar (03-06)
    ADD COLUMN notes_index      BYTEA;       -- index cec (apartat 5)

-- La columna en clar s elimina DESPRES de migrar i verificar
-- ALTER TABLE cites DROP COLUMN notes;

CREATE INDEX idx_cites_notes_index ON cites (tenant_id, notes_index);
import secrets
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

VERSIO = b"\x01"

def xifrar_notes(text: str, tenant_id: str, cita_id: int, kms) -> dict:
    # 1. XIFRATGE DE SOBRE (03-06): una clau de dades NOVA per registre
    dk_clar, dk_xifrada, kid = kms.generar_clau_dades()

    # 2. AAD amb el context: lliga el ciphertext al seu amo (03-02)
    aad = f"v1|tenant:{tenant_id}|cita:{cita_id}".encode()
    nonce = secrets.token_bytes(12)
    ct = AESGCM(dk_clar).encrypt(nonce, text.encode("utf-8"), aad)

    del dk_clar                       # 3. fora de memoria com abans millor
    return {
        "notes_xifrades": VERSIO + nonce + ct,
        "notes_clau_dk": dk_xifrada,
        "notes_kms_kid": kid,
    }

def desxifrar_notes(fila, tenant_id: str, cita_id: int, kms) -> str:
    dk_clar = kms.desxifrar_clau_dades(fila["notes_clau_dk"],
                                       fila["notes_kms_kid"])
    blob = fila["notes_xifrades"]
    assert blob[:1] == VERSIO, "versio de xifratge desconeguda"
    aad = f"v1|tenant:{tenant_id}|cita:{cita_id}".encode()
    try:
        return AESGCM(dk_clar).decrypt(blob[1:13], blob[13:], aad).decode()
    finally:
        del dk_clar

Les decisions i per què són així:

  • Xifratge de sobre per registre. Cada nota té la seva pròpia clau de dades, xifrada amb la mestra del KMS. Així rotar la mestra no exigeix tornar a xifrar el contingut, i cada operació de desxifratge deixa auditoria al KMS (03-06): si algú desxifra deu mil notes en una hora, es veu.
  • AAD amb tenant_id i cita_id. Moure la fila a un altre tenant o copiar-la a una altra cita invalida el desxifratge. És l'extensió criptogràfica de l'aïllament multi-tenant de 02-05.
  • Byte de versió. Permet canviar d'algorisme d'aquí a tres anys sense migrar les dades antigues de cop.
  • del dk_clar. Redueix la finestra en què la clau és a la memòria. No és una garantia forta en Python —el recol·lector decideix—, però és la pràctica correcta i evita que la clau acabi en un bolcat d'excepció o en un registre de depuració.
  • La columna en clar s'elimina al final, després de migrar i verificar. I cal recordar que esborrar-la no esborra les còpies de seguretat anteriors, que continuen contenint el text en clar fins que expiri la seva retenció.

  1. El problema que introdueix: ja no es pot cercar

Aquí hi ha el cost real del xifratge a nivell d'aplicació, i és la raó per la qual no s'aplica a tot:

-- Aixo ja NO funciona: el motor nomes veu bytes indistingibles de l atzar
SELECT * FROM cites WHERE notes ILIKE '%genoll%';

En xifrar bé, el text xifrat és indistingible de dades aleatòries. Això és precisament el que volem —i el que impedeix cercar, ordenar, indexar, agrupar o fer coincidències parcials—. Les opcions i els seus compromisos:

Opció Què permet Què costa
Cercar només per metadades no xifrades Filtrar per tenant, data, professional, estat Cap cerca per contingut. L'opció més segura
Índex cec amb HMAC determinista Coincidència exacta sobre valors concrets Revela igualtat: dos registres amb el mateix valor comparteixen índex, cosa que permet anàlisi de freqüències
Desxifrar a l'aplicació i filtrar Cerca completa Inviable amb volum: cal portar i desxifrar-ho tot
Xifratge que preserva l'ordre o xifratge cercable Rangs i cerques Fuites d'informació conegudes i complexitat alta. No recomanable per a una pime

L'índex cec, que és la solució pràctica més habitual, funciona així: en lloc de xifrar el valor pel qual es vol cercar, es calcula un HMAC determinista amb una clau dedicada, i s'indexa aquell resultat. Com que l'HMAC és determinista, el mateix valor produeix sempre el mateix índex; i com que porta clau, ningú sense ella no el pot calcular ni construir un diccionari.

import hmac, hashlib

def index_cec(valor: str, tenant_id: str, clau_index: bytes) -> bytes:
    """HMAC determinista per a cerca exacta. La clau es DIFERENT de la
    de xifratge, derivada amb HKDF (03-02). El tenant_id entra al missatge
    perque el mateix valor en dues cliniques produeixi indexs diferents."""
    normalitzat = valor.strip().casefold()          # mateixa forma -> mateix index
    missatge = f"{tenant_id}|{normalitzat}".encode("utf-8")
    return hmac.new(clau_index, missatge, hashlib.sha256).digest()[:16]
-- Cerca exacta sobre l index, sense desxifrar res
SELECT id, data FROM cites
 WHERE tenant_id = $1 AND notes_index = $2;

Tres detalls que fan que això sigui correcte i no un adorn: la clau de l'índex és diferent de la de xifratge, derivada amb HKDF, perquè comprometre'n una no doni l'altra; el tenant_id entra al missatge, de manera que el mateix valor en dues clíniques produeix índexs diferents i es talla l'anàlisi entre tenants; i la normalització prèvia garanteix que "Genoll" i " genoll " coincideixin, perquè l'HMAC no perdona ni un espai.

I el seu límit, que cal assumir conscientment: revela igualtat. Un atacant amb accés a la taula veu quines files comparteixen valor i pot fer inferències per freqüència. Per això l'índex cec s'aplica a camps amb alta cardinalitat —una referència, un identificador— i no a camps amb pocs valors possibles, on la freqüència ho delataria tot.

Decisió per a Nimbus: xifrar el cos de les notes clíniques i no permetre cerca pel seu contingut; la cerca es fa per metadades (pacient, professional, data) que no són la dada més sensible. L'índex cec es reserva per a l'identificador de pacient, on cal coincidència exacta.


  1. Xifratge de còpies de seguretat

A 02-04 vas establir l'esquema 3-2-1-1-0, amb una còpia immutable. Aquesta lliçó hi afegeix el requisit que faltava: aquella còpia ha d'estar a més xifrada, i la seva clau ha de viure fora del mateix entorn.

El raonament surt directament del cas de ransomware de 02-06. Allà l'atacant va entrar per l'accés remot de la consultora, va escalar amb un secret oblidat i va destruir unes còpies que eren al mateix compte que producció. Afegeix-hi ara la variant moderna, la doble extorsió: no n'hi ha prou de xifrar les teves dades, també se les emporten i amenacen amb publicar-les.

Propietat de la còpia Davant de quina amenaça
Immutable (02-04) Que l'atacant esborri o xifri les còpies
Xifrada Que l'atacant llegeixi i publiqui el seu contingut
Clau fora de l'entorn Que el mateix compromís que dona accés a la còpia doni també la clau
Restauració provada Que la còpia existeixi i no serveixi

Les tres regles pràctiques: la clau de les còpies viu en un altre compte o proveïdor, amb accés independent i no accessible des dels rols de producció; mai al costat de la còpia, que és l'error més freqüent; i la restauració s'assaja almenys una vegada l'any, incloent-hi el pas de recuperar la clau des de la seva custòdia (03-06), perquè una custòdia no provada és una suposició.


  1. Signatura de codi i artefactes al CI/CD

De SolarWinds (02-06) va sortir una lliçó incòmoda: els atacants van comprometre el procés de compilació, així que les actualitzacions malicioses anaven signades amb la clau legítima de l'empresa i els clients les van instal·lar amb tota confiança. La conclusió que en vas extreure aleshores continua vigent: una signatura vàlida acredita l'origen, no l'absència de malícia.

Aleshores, per a què signar? Perquè sense signatura no tens cap de les dues coses. Signar dona tres garanties concretes i verificables:

Garantia Què significa
Integritat L'artefacte no s'ha modificat des que es va construir
Origen El va produir el teu CI/CD i no un tercer
Traçabilitat Amb la procedència signada, pots dir de quin commit, amb quines dependències i en quina execució va sortir
# .github/workflows/publicar.yml (fragment)
permissions:
  contents: read
  id-token: write          # identitat efimera per signar SENSE claus desades
  packages: write

steps:
  - name: Construir imatge
    run: docker build -t ghcr.io/nimbus/api:${{ github.sha }} .

  - name: Generar SBOM (02-04)
    run: syft ghcr.io/nimbus/api:${{ github.sha }} -o spdx-json > sbom.json

  - name: Signar imatge i SBOM
    run: |
      cosign sign --yes ghcr.io/nimbus/api:${{ github.sha }}
      cosign attest --yes --predicate sbom.json \
        ghcr.io/nimbus/api:${{ github.sha }}
# Al desplegament: NO desplegar res que no es verifiqui
cosign verify \
  --certificate-identity-regexp "https://github.com/nimbus/api/.github/workflows/.*" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  ghcr.io/nimbus/api:$SHA

L'important d'aquest esquema: no hi ha cap clau privada de signatura desada enlloc. Es fa servir una identitat efímera del mateix CI (l'id-token), se signa amb un parell que existeix uns segons i la prova queda ancorada a un registre públic de transparència, amb la mateixa lògica de la CT de 03-06. És la millor resposta al problema de l'apartat anterior: elimina el risc que robin la clau de signatura, perquè no hi ha clau per robar. I la verificació al desplegament comprova la identitat del signant, no només que la signatura sigui vàlida: sense --certificate-identity-regexp, acceptaries la signatura de qualsevol.

Integritat de dependències. L'altre extrem del mateix problema és el que entra a la teva compilació. La defensa és fixar hashos, de manera que una versió republicada amb contingut diferent no s'instal·li:

# requirements.txt amb hashos fixats
cryptography==43.0.1 \
    --hash=sha256:8f1e2c9a...  \
    --hash=sha256:2d7b40f3...
pip install --require-hashes -r requirements.txt

--require-hashes fa que la instal·lació falli si el paquet descarregat no coincideix amb el hash declarat. És una aplicació directa de les funcions hash de 03-04 al risc de cadena de subministrament.


  1. Tokens signats i URL signades per dins

Signat no és xifrat: JWS davant de JWE

A 02-05 vas fer servir JWT sense entrar en la seva criptografia. Aquí només interessa aquella part, i una distinció que es confon constantment:

JWS (signat) JWE (xifrat)
Què garanteix Integritat i autenticitat Confidencialitat (i també integritat, si és AEAD)
El contingut és llegible? Sí. Base64 és codificació, no xifratge (03-01) No
Ús habitual El 99 % dels JWT Quan el token ha de portar dades que el client no pot veure
Algorismes EdDSA, ES256, RS256, HS256 Combinacions de gestió de clau + AEAD

La conseqüència pràctica és molt concreta: un JWT normal és llegible per qualsevol que el tingui. Qualsevol el pot enganxar en un visor i veure'n el contingut. Per això mai no es fiquen dades personals ni secrets en un token signat: s'hi fiquen identificadors opacs. Un sub: "u_88421" és correcte; un email: "ana.ruiz@..." és una fuita que viatjarà a cada petició i acabarà en algun registre.

I l'elecció d'algorisme: EdDSA o ES256 (asimètrics) quan el verificador és un altre sistema, perquè només necessita la clau pública i no pot fabricar tokens; HS256 (simètric) només si emissor i verificador són el mateix servei, perquè compartir la clau HMAC equival a compartir la capacitat d'emetre. És la distinció entre MAC i signatura de 03-03 aplicada a sessions.

Les URL signades de 120 segons, per dins

Fa tot el curs que veus aquesta peça sense obrir-la. Una URL signada permet que el navegador descarregui un adjunt directament del bucket A-05, sense que el bucket sigui públic i sense que el fitxer passi per l'API. El que se signa és exactament això:

Missatge que entra a la signatura:
  metode   : GET
  recurs   : /nimbus-adjuntos/CL-014/9f31c7.pdf
  caduca   : 1785938400          (marca de temps UNIX)
  capcaleres: host
import hmac, hashlib, time, base64

def url_signada(recurs: str, clau: bytes, segons: int = 120) -> str:
    caduca = int(time.time()) + segons
    missatge = f"GET\n{recurs}\n{caduca}".encode("utf-8")
    signatura = hmac.new(clau, missatge, hashlib.sha256).digest()
    signatura_b64 = base64.urlsafe_b64encode(signatura).decode().rstrip("=")
    return f"https://cdn.nimbusreservas.example{recurs}?exp={caduca}&sig={signatura_b64}"

def validar(recurs: str, exp: str, sig: str, clau: bytes) -> bool:
    if int(exp) < time.time():            # 1. caducitat ABANS que la signatura
        return False
    missatge = f"GET\n{recurs}\n{exp}".encode("utf-8")
    esperada = base64.urlsafe_b64encode(
        hmac.new(clau, missatge, hashlib.sha256).digest()
    ).decode().rstrip("=")
    return hmac.compare_digest(esperada, sig)   # 2. temps constant (03-04)

Per què està dissenyada així, punt per punt:

  • La caducitat és dins del missatge signat. Si fos fora, l'usuari podria canviar exp a un any vista i l'URL continuaria valent. En ser a dins, alterar-la invalida la signatura.
  • El recurs també. Sense ell, una signatura vàlida per a un adjunt serviria per a qualsevol altre: seria un IDOR amb esteroides.
  • El mètode. Impedeix convertir un permís de lectura en un d'escriptura.
  • 120 segons. És el temps just perquè el navegador iniciï la descàrrega. Les URL acaben a l'historial, en registres de proxy, en captures de pantalla i en missatges reenviats; una caducitat curta fa que tot això sigui inofensiu minuts després.
  • hmac.compare_digest. Temps constant, com al webhook de 03-04.
  • L'autorització es comprova abans de signar, no després. L'API verifica que l'usuari té accés a aquell adjunt d'aquell tenant, i només aleshores emet l'URL. La signatura no autoritza: transporta una autorització ja concedida.

  1. Pseudonimització i anonimització

Nimbus necessita dades per a l'entorn de proves i per a la seva analítica de producte, i la temptació és copiar producció «traient els noms». Aquí la precisió terminològica té conseqüències reals.

Pseudonimització Anonimització
Definició Substituir identificadors per altres valors, de manera que continua sent possible reidentificar amb informació addicional Transformar les dades de manera que la reidentificació sigui impossible, per a qualsevol i de manera irreversible
És reversible? Sí, amb la clau o la taula de correspondència No
Tècniques Tokenització, hash amb sal secreta, xifratge Agregació, generalització, supressió, privacitat diferencial
Estatus Continuen sent dades personals Deixen de ser-ho
Ús a Nimbus Entorns de prova, suport, analítica interna Estadístiques publicables

Per què el hash d'un DNI no és anonimització. És l'error conceptual més car d'aquest apartat. Un DNI espanyol té un espai de valors petit i una estructura coneguda: generar el hash de tots els DNI possibles i construir la taula inversa és qüestió de minuts. El mateix val per a un telèfon, un correu o una matrícula. Un hash sense clau sobre un identificador de domini acotat és reversible a la pràctica, i per tant continua sent una dada personal.

Què fer al seu lloc, segons l'objectiu:

Objectiu Tècnica correcta
Entorn de proves realista Generar dades sintètiques. L'opció preferible: no hi ha dada personal que protegir
Si cal partir de dades reals Tokenització reversible amb taula de correspondència en un servei a part i d'accés molt restringit, o xifratge amb clau que no sigui a l'entorn de proves
Analítica interna per usuari HMAC amb clau secreta (no hash a seques), amb clau diferent per finalitat i rotada
Estadística publicable Agregació amb llindar mínim (no publicar cel·les amb menys de N casos), generalització de rangs i supressió de valors rars

Nota de validació professional. La frontera entre pseudonimitzar i anonimitzar és jurídica a més de tècnica, i l'anonimització real és difícil d'assolir: la combinació de camps aparentment innocus —codi postal, data de naixement i sexe— reidentifica una part substancial d'una població. Abans de declarar un conjunt de dades «anonimitzat» i tractar-lo com a no personal, valida-ho amb el responsable de protecció de dades i amb assessoria jurídica. El marc de l'RGPD es tracta a 06-03.


  1. Casos que se solen fer malament

Xifrar i desar la clau al costat. L'error número u. La còpia xifrada i la seva clau al mateix bucket; la clau de camp en una columna de la mateixa base de dades; la clau del disc al mateix repositori que el docker-compose. En tots aquests casos, l'atacant que arriba a la dada arriba a la clau, i el xifratge no ha afegit res tret d'una falsa tranquil·litat. La pregunta de control és sempre la mateixa: quin compromís concret hauria de passar perquè algú obtingués dada i clau alhora? Si la resposta és «un de sol», cal redissenyar.

Xifrar per complir, sense model d'amenaça. S'activa el xifratge de disc, s'escriu «dades xifrades en repòs» al qüestionari del client i es dona l'assumpte per resolt. Però el xifratge de disc no atura cap de les amenaces reals de Nimbus —IDOR, credencials robades, token filtrat, bucket mal configurat, accés excessiu de la consultora—. Xifrar és una resposta; la pregunta és davant de qui. Sense aquella pregunta, es compra tranquil·litat en lloc de seguretat.

Confondre xifratge amb control d'accés. Són capes diferents i no se substitueixen. L'IDOR de 01-01 passava sobre TLS impecable i amb la base de dades xifrada en repòs; es va corregir amb un WHERE tenant_id, no amb més criptografia. El xifratge decideix què es pot llegir sense la clau; el control d'accés decideix qui obté la clau o el resultat. Un sistema que ho xifra tot i autoritza malament està trencat.

Tres errors menors però freqüents: xifrar el camp i deixar la mateixa dada en un registre o en un correu de notificació; no versionar el format, cosa que converteix qualsevol canvi futur en una migració de tot o res; i no tenir pla de rotació, que és el que fa que una clau duri set anys.


  1. Taula de referència: dada, amenaça, protecció i clau

Dada de Nimbus Amenaça principal Protecció criptogràfica On viu la clau
Trànsit app/SPA ↔ API Escolta i interceptació en xarxa pública TLS 1.3 amb ECDHE i AEAD; HSTS Efímera per sessió (forward secrecy)
Contrasenyes d'usuaris Bolcat de la taula de credencials Argon2id amb sal única i rehash No hi ha clau; hi ha paràmetres (pepper opcional al gestor)
Sessions i tokens Falsificació o manipulació d'un token JWS amb EdDSA i kid Privada al gestor; pública al JWKS
Adjunts del bucket A-05 Accés a l'emmagatzematge; moviment entre tenants AES-256-GCM amb AAD del tenant, xifratge de sobre Clau de dades per objecte; mestra al KMS
Descàrrega d'un adjunt Enllaç reenviat o filtrat en registres URL signada amb HMAC, caducitat de 120 s Clau de signatura al gestor de secrets
Notes clíniques Bolcat, injecció SQL, còpia restaurada fora AES-256-GCM camp a camp amb sobre Clau de dades per registre; mestra al KMS
Resta de la base de dades Robatori del suport o del volum Xifratge de disc i de base de dades Gestionada pel proveïdor
Còpies de seguretat Ransomware amb doble extorsió Xifratge + immutabilitat + verificació de hash Fora de producció, amb custòdia repartida
Webhook de la passarel·la Notificació de pagament falsificada HMAC-SHA256 amb marca de temps Secret compartit, al gestor
Correu sortint Suplantació del domini DKIM (amb SPF i DMARC) Privada al proveïdor de correu; pública al DNS
Artefactes del CI/CD Cadena de subministrament (SolarWinds) Signatura amb identitat efímera + procedència + hashos fixats No hi ha clau persistent: identitat OIDC del CI
Accés administratiu Robatori de credencials SSH amb Ed25519; mTLS entre serveis Privada al dispositiu; certificats de la PKI interna
Dades de l'entorn de proves Còpia de producció sense protegir Dades sintètiques o tokenització reversible Correspondència en un servei a part, fora de proves

Aquesta taula és el resum executiu del mòdul sencer i l'artefacte que hauries de saber produir per a qualsevol sistema en què treballis.


Errors Comuns i Consells

  1. Comptar el xifratge de disc com a defensa davant d'atacs a l'aplicació. No ho és. Activa'l sempre, però no l'apuntis a la columna equivocada.
  2. Xifrar i desar la clau al costat. Aplica la pregunta de control: un sol compromís dona dada i clau?
  3. Xifrar sense model d'amenaça. Escriu primer de qui protegeixes.
  4. Confondre xifratge amb autorització. L'IDOR no s'arregla xifrant.
  5. Ficar dades personals en un JWT. Signat no és xifrat: és llegible per qualsevol.
  6. Fer servir HS256 quan el verificador és un altre sistema. Compartir la clau HMAC és regalar la capacitat d'emetre.
  7. Signar una URL sense incloure la caducitat i el recurs al missatge. Es pot allargar o reapuntar.
  8. Emetre una URL signada sense comprovar abans l'autorització. La signatura transporta permís, no el concedeix.
  9. Anomenar anonimització un hash d'un identificador. És reversible i continua sent dada personal.
  10. Xifrar un camp i deixar la mateixa dada en un registre, un correu o una còpia antiga.
  11. No versionar el format xifrat. Condemna qualsevol canvi futur a una migració total.
  12. Verificar la signatura d'un artefacte sense comprovar la identitat del signant.

Consells

  • Construeix la taula de l'apartat 11 per al teu propi sistema. Les caselles que no sàpigues emplenar són exactament la teva feina pendent.
  • Xifra a nivell d'aplicació només el que ho mereix. Cada camp xifrat és funcionalitat perduda i complexitat guanyada; pagar-ho per tot és la via més ràpida a un sistema inmanejable.
  • Dissenya des del principi la rotació i el format versionat. Afegir-los després costa deu vegades més.
  • Recorda la jerarquia real d'eficàcia per a una pime: control d'accés > gestió de claus > elecció d'algorisme. El mòdul ha anat en ordre invers per raons didàctiques, però l'impacte va en aquest.

Exercicis

Exercici 1 — Respondre a la pregunta del client

Una clínica clienta envia un qüestionari de seguretat amb aquestes afirmacions i demana a Nimbus que confirmi o matisi cadascuna:

  1. «Les dades estan xifrades en repòs, per la qual cosa un atacant no les pot llegir.»
  2. «Feu servir HTTPS, per la qual cosa les dades dels nostres pacients estan segures.»
  3. «Les contrasenyes estan xifrades a la vostra base de dades.»
  4. «Les vostres còpies de seguretat estan xifrades, per la qual cosa un ransomware no és un risc.»
  5. «Les dades que feu servir per a proves estan anonimitzades.»

Es demana: per a cada afirmació, indica si és correcta, incorrecta o incompleta; explica amb precisió tècnica per què; i redacta la resposta que Nimbus hauria de donar —honesta, sense màrqueting i comprensible per a un lector no tècnic—.

Exercici 2 — Dissenyar el xifratge d'un camp nou

Nimbus afegeix un camp allergies a la fitxa de pacient, visible per al professional durant la cita.

Es demana: (a) decideix si s'ha de xifrar a nivell d'aplicació i justifica-ho; (b) dissenya l'esquema complet: columnes, algorisme, contingut de l'AAD, on viu la clau i com es rota; (c) resol el requisit que el professional pugui filtrar les seves cites del dia per presència d'al·lèrgies, sense exposar el contingut; (d) enumera els tres efectes col·laterals que el xifratge provocarà a la resta del sistema (registres, còpies, suport) i com els abordes; (e) indica quina validació no tècnica cal abans de desplegar-lo.

Exercici 3 — Auditar un flux de descàrrega

Aquest és el codi de descàrrega d'adjunts de Nimbus:

@app.get("/api/v1/adjuntos/{adjunt_id}/url")
def obtenir_url(adjunt_id: str, usuari = Depends(usuari_actual)):
    recurs = f"/nimbus-adjuntos/{adjunt_id}.pdf"                # (1)
    exp = int(time.time()) + 86400                              # (2)
    sig = hashlib.sha256(f"{recurs}{CLAU}".encode()).hexdigest()    # (3)
    logger.info("URL emesa: %s?exp=%s&sig=%s", recurs, exp, sig)    # (4)
    return {"url": f"https://cdn.nimbus.example{recurs}?exp={exp}&sig={sig}"}

Es demana: (a) identifica els problemes de les quatre línies marcades i l'atac concret de cadascuna; (b) assenyala el problema més greu, que no està marcat; (c) reescriu l'endpoint correctament; (d) indica quin esdeveniment registraries i amb quins camps.


Solucions

Exercici 1

# Veredicte Per què Resposta de Nimbus
1 Incompleta El xifratge en repòs atura el robatori del suport, no un accés a través de l'aplicació amb credencials vàlides. Amb el sistema encès, el motor desxifra per a qui pregunta «Sí, xifrem en repòs a nivell de disc i de base de dades, i a més xifrem camp a camp les dades clíniques. Convé precisar que el xifratge en repòs protegeix davant del robatori del suport; davant d'un accés indegut a través de l'aplicació, la protecció és el control d'accés, l'aïllament entre clíniques i la detecció, que descrivim a part»
2 Incompleta HTTPS protegeix el tram de xarxa. No protegeix davant de fallades de l'aplicació com IDOR o injecció, ni davant d'un client compromès «Sí, tot el trànsit va per TLS 1.3 amb HSTS. HTTPS garanteix que ningú no pot llegir ni alterar les dades pel camí; la protecció de les dades un cop arriben depèn d'altres controls, que detallem»
3 Incorrecta Les contrasenyes no es xifren: es deriven amb una funció lenta i unidireccional. Si es xifressin, existiria una clau capaç de recuperar-les totes «Corregim el terme: no les xifrem, les processem amb Argon2id, amb sal única per usuari i paràmetres revisats periòdicament. És un procés irreversible: ni tan sols nosaltres podem recuperar una contrasenya»
4 Incompleta El xifratge protegeix davant de la publicació de les dades (doble extorsió), no davant de l'esborrament o xifratge de les còpies. Per a això cal immutabilitat, aïllament i restauració provada «Les còpies estan xifrades i són immutables, amb la clau fora de l'entorn de producció i restauracions provades periòdicament. El xifratge cobreix el risc de publicació; la immutabilitat, el de destrucció. Són mesures complementàries»
5 Probablement incorrecta Si la tècnica és un hash d'identificadors, és pseudonimització reversible a la pràctica, i continuen sent dades personals «Fem servir dades sintètiques en proves i, quan partim de dades reals, tokenització amb la correspondència custodiada a part. Evitem el terme "anonimitzades" perquè exigeix irreversibilitat demostrable; el correcte és "pseudonimitzades", i per això l'entorn de proves manté controls equivalents a producció»

Exercici 2

(a) S'ha de xifrar? Sí. Una al·lèrgia és informació de salut, del mateix nivell de sensibilitat que les notes clíniques, i el criteri de l'apartat 3 és clar: el xifratge a nivell d'aplicació es reserva per al més sensible, i això ho és.

(b) Esquema. Columnes allergies_xifrades BYTEA, allergies_clau_dk BYTEA, allergies_kms_kid TEXT i allergies_presencia BOOLEAN. Algorisme AES-256-GCM amb xifratge de sobre i clau de dades per registre. AAD: v1|tenant:{tenant_id}|pacient:{pacient_id}. Clau mestra al KMS, rotada anualment; com que el sobre desacobla la mestra del contingut, rotar-la només torna a xifrar les claus de dades.

(c) Filtrar per presència sense exposar el contingut. Una columna booleana no xifrada allergies_presencia. Revela únicament si hi ha al·lèrgies registrades —una metadada de sensibilitat molt menor— i permet un índex i un filtre trivials, sense desxifrar res. És preferible a un índex cec, que aquí no aportaria res (el requisit no és coincidència exacta) i revelaria igualtat entre pacients. Important: cal decidir conscientment que aquell booleà és acceptable, perquè també és informació.

(d) Tres efectes col·laterals. (1) Registres: qualsevol logger que bolqui la fila completa reintrodueix la dada en clar; cal revisar el registre (02-04) i excloure el camp explícitament. (2) Còpies de seguretat: les anteriors a la migració contenen el text en clar fins que expiri la seva retenció, cosa que s'ha de documentar. (3) Suport: el Rubén deixarà de veure el camp a les eines internes, cosa que exigeix un procediment explícit —desxifratge sota permís concret, registrat i auditat— en lloc d'accés general.

(e) Validació no tècnica. Abans de desplegar cal validar amb el responsable de protecció de dades i amb assessoria jurídica: base jurídica del tractament, informació a l'interessat, termini de conservació, qui hi pot accedir i en quines circumstàncies, i actualització del registre d'activitats i de l'anàlisi de riscos (06-03).

Exercici 3

(a) Problemes marcats:

# Problema Atac
1 El recurs es construeix amb l'adjunt_id que envia el client, sense validar-ne el format Recorregut de rutes: un identificador amb ../ pot apuntar a altres objectes del bucket
2 Caducitat de 86.400 s (24 h) Una URL reenviada, desada a l'historial o filtrada en un registre continua servint un dia sencer
3 sha256(recurs + CLAU) en lloc d'HMAC, i sense incloure exp ni el mètode al missatge Construcció insegura (03-04) i, sobretot, exp no està signat: l'usuari el canvia a un any vista i l'URL continua sent vàlida
4 Es registra l'URL completa amb la signatura El registre es converteix en un magatzem de credencials d'accés: qui llegeixi els logs descarrega els adjunts

(b) El problema més greu, no marcat: no hi ha cap comprovació d'autorització. La funció rep l'usuari_actual i no el fa servir. Qualsevol usuari autenticat de qualsevol clínica pot demanar una URL per a l'adjunt de qualsevol altra. És l'IDOR de 01-01 reaparegut en un endpoint nou, i cap millora criptogràfica no ho arregla.

(c) Versió correcta:

@app.get("/api/v1/adjuntos/{adjunt_id}/url")
def obtenir_url(adjunt_id: str, usuari = Depends(usuari_actual)):
    # 1. AUTORITZACIO PRIMER: l adjunt ha de ser del tenant de l usuari
    adjunt = repo.buscar_adjunt(adjunt_id, tenant_id=usuari.tenant_id)
    if adjunt is None:
        raise HTTPException(404, "No trobat")         # mateix error que si no existeix

    # 2. Recurs construit a partir de dades de CONFIANCA (de la BD)
    recurs = f"/nimbus-adjuntos/{adjunt.tenant_id}/{adjunt.clau_objecte}"

    # 3. Signatura HMAC sobre metode + recurs + caducitat, amb 120 s de vida
    url = url_signada(recurs, CLAU_SIGNATURA_URL, segons=120)

    # 4. Registre SENSE la signatura ni l URL completa
    registrar_esdeveniment("adjunt.url_emesa",
                           usuari_id=usuari.id, tenant_id=usuari.tenant_id,
                           adjunt_id=adjunt.id, caducitat_s=120)
    return {"url": url}

(d) Esdeveniment a registrar. Camps: marca de temps, usuari_id, tenant_id, adjunt_id, IP d'origen, caducitat concedida i resultat. Mai: l'URL completa, la signatura, el nom del fitxer si és descriptiu del contingut clínic, ni cap dada del pacient. Sobre aquell registre es construeix una alerta útil: un mateix usuari sol·licitant moltes URL d'adjunts diferents en poc temps és el patró d'una exfiltració en curs, exactament el tipus de senyal que faltava en el cas de 02-06.


Conclusió

Has tancat el mòdul integrant tot l'anterior sobre un sistema real. Tens el mapa criptogràfic de Nimbus de punta a punta, amb els seus nou trams i amb el detall que més importa: el KMS no és al camí de les dades sinó al de les claus. Distingeixes trànsit i repòs, i saps que TLS acaba on comença el servidor. I te'n portes l'apartat que desfà més malentesos: els tres nivells del xifratge en repòs —disc, base de dades i aplicació—, amb la conclusió que el xifratge de disc protegeix davant del robatori del maquinari i no protegeix res davant d'una API compromesa, una injecció SQL o un IDOR, perquè el sistema està encès i el motor desxifra encantat per a qui pregunta.

Has xifrat camp a camp les notes clíniques amb xifratge de sobre, AAD del tenant i format versionat, i n'has pagat el preu honestament: ja no es pot cercar, amb les opcions sobre la taula —metadades, índex cec amb HMAC determinista i les seves fuites per freqüència, o desxifrar i filtrar— i una decisió raonada per a Nimbus. Has afegit el requisit que faltava a les còpies 3-2-1-1-0: xifrades, amb la clau fora de l'entorn, perquè la immutabilitat cobreix la destrucció i el xifratge cobreix la publicació en la doble extorsió. Has signat els artefactes del CI/CD amb identitat efímera —sense clau per robar— verificant la identitat del signant i fixant hashos de dependències, amb SolarWinds com a recordatori que una signatura vàlida acredita l'origen i no l'absència de malícia. Has obert per dins els tokens signats, amb la distinció entre JWS i JWE i la regla de no ficar dades personals en una cosa que qualsevol pot llegir, i les URL signades de 120 segons, entenent per què la caducitat i el recurs van dins del missatge signat i per què la signatura transporta una autorització, no la concedeix. I has separat pseudonimització d'anonimització, amb el motiu pel qual el hash d'un DNI és reversible a la pràctica i continua sent una dada personal. Tot això condensat a la taula final dada → amenaça → protecció → on viu la clau, que és l'artefacte que hauries de saber produir per a qualsevol sistema.

Tanca el mòdul l'advertiment que el recorre sencer: xifrar i desar la clau al costat, xifrar per complir sense model d'amenaça i confondre xifratge amb control d'accés són els tres errors que converteixen un desplegament criptogràfic en tranquil·litat falsa. I amb ells, la jerarquia real d'eficàcia per a una pime com Nimbus: control d'accés, gestió de claus i, en últim lloc, elecció d'algorisme.

Amb això tens el catàleg complet del que es pot protegir i com. Però han quedat sense resposta les preguntes que de debò decideix una direcció: de tot això, què fem primer? Nimbus té 38 persones, un pressupost limitat i una llista de millores que no cap en un any. Quant val evitar la fuita de les notes clíniques davant d'evitar una caiguda de dos dies? Quins riscos s'accepten conscientment i qui signa aquella acceptació? Com es posa per escrit perquè la decisió sobrevisqui a la persona que la va prendre?

Al Mòdul 4: Gestió de Riscos i Mesures de Protecció deixem de preguntar com es protegeix i comencem a preguntar què val la pena protegir i amb quina prioritat. Començarem per l'avaluació de riscos (04-01), que és el mètode per convertir aquella llista inabastable en un ordre defensable, i seguirem amb les polítiques que ho fixen per escrit, els controls, el risc de tercers —on tornarà la consultora de l'incident de 02-06—, el pla de resposta i la recuperació davant desastres.

Curs de Fonaments de Seguretat Informàtica

Mòdul 1: Introducció a la Seguretat Informàtica

Mòdul 2: Ciberseguretat

Mòdul 3: Criptografia

Mòdul 4: Gestió de Riscos i Mesures de Protecció

Mòdul 5: Eines i Tècniques de Seguretat

Mòdul 6: Bones Pràctiques i Normatives

Mòdul 7: Projecte Final

© Copyright 2026. Tots els drets reservats