La lliçó anterior et va deixar amb un mapa de primitives i una promesa: començar per la més antiga i la més usada, la que empra una única clau compartida per xifrar i desxifrar. És la que mou el 99 % dels bytes xifrats del planeta —el contingut de cada connexió TLS, cada disc xifrat, cada còpia de seguretat de Nimbus— perquè és ràpida, senzilla de raonar i està resolta des de fa dècades. Aquesta lliçó explica per què AES és l'estàndard, què és un mode d'operació i per què triar-lo malament arruïna un algorisme intatxable, què és un nonce i per què repetir-lo és una catàstrofe, i com es xifra correctament un adjunt de Nimbus amb AES-GCM. Acaba al punt exacte on el simètric es queda sense respostes: com fer arribar la clau a l'altre extrem.
Contingut
- Què és la criptografia simètrica i quan es fa servir
- Xifratge de bloc i xifratge de flux
- AES: per què és l'estàndard
- Modes d'operació: per què un bloc no basta
- ECB, CBC i CTR: el catàleg clàssic i els seus paranys
- Xifratge autenticat (AEAD) i dades addicionals autenticades
- Xifrar un adjunt de Nimbus amb AES-GCM
- La regla del nonce
- Xifrar una còpia de seguretat amb
openssldes de la línia d'ordres - Derivació de claus: HKDF
- Rendiment i les seves conseqüències de disseny
- El problema que queda obert: distribuir la clau
- Què és la criptografia simètrica i quan es fa servir
En un esquema simètric, la mateixa clau serveix per xifrar i per desxifrar. Emissor i receptor —o, molt sovint, la mateixa màquina en dos moments diferents— comparteixen aquest secret.
flowchart LR
P["TEXT EN CLAR\nadjunt de la clinica"] --> C["XIFRAR\nAES-GCM"]
K["CLAU UNICA\n256 bits"] --> C
C --> X["TEXT XIFRAT\n+ nonce + etiqueta"]
X --> D["DESXIFRAR\nAES-GCM"]
K --> D
D --> P2["TEXT EN CLAR\nrecuperat"]
Els escenaris on és la resposta natural comparteixen un tret: la clau ja està disponible als dos extrems, sigui perquè només hi ha un extrem o perquè un altre mecanisme l'hi va posar.
| Escenari a Nimbus | Per què és simètric |
|---|---|
| Xifratge d'adjunts al bucket A-05 | Xifra i desxifra el mateix sistema, amb la clau del gestor de secrets |
| Còpies de seguretat xifrades (02-04) | Es xifren avui i es desxifren mesos després: un únic posseïdor de la clau |
| Xifratge en repòs de PostgreSQL i dels discos | Transparent, alt volum, alta velocitat |
| Contingut d'una sessió TLS | La clau la van acordar abans per mitjans asimètrics (03-03, 03-05) |
| Notes clíniques xifrades camp a camp | Xifra i desxifra l'API; vegeu 03-07 |
Els seus dos avantatges són la velocitat —ordres de magnitud sobre l'asimètrica, amb instruccions específiques als processadors actuals— i les claus curtes: 256 bits basten per al molt llarg termini. La seva única gran mancança és la distribució: si dues parts que no s'han vist mai necessiten una clau comuna, el simètric tot sol no els la pot donar; i el nombre de claus creix de manera quadràtica, perquè n parts que vulguin parlar per parelles necessiten n·(n−1)/2 claus (amb 38 empleats, 703).
- Xifratge de bloc i xifratge de flux
Hi ha dues maneres de construir un xifrador simètric:
| Xifratge de bloc | Xifratge de flux | |
|---|---|---|
| Com opera | Sobre trossos de mida fixa (128 bits en AES) | Genera un raig de bytes pseudoaleatoris i els combina amb el text (XOR) |
| Necessita farciment | Sí, en alguns modes | No: el xifrat té la mateixa mida que el clar |
| Exemples vigents | AES | ChaCha20 |
| Exemples prohibits | DES, 3DES (obsolets) | RC4 (trencat) |
| Risc característic | Un mode mal triat revela estructura | Reutilitzar el raig amb la mateixa clau i nonce és fatal |
La frontera entre tots dos és més difusa del que sembla: un xifrador de bloc en mode CTR es comporta com un de flux, perquè en lloc de xifrar les dades genera un raig que després es combina amb elles. Aquesta és exactament la idea sobre la qual es construeix AES-GCM. I tots dos comparteixen la regla que arrosseguem des de Vigenère: el mateix material de clau no es fa servir dues vegades. En un xifratge de flux això és literal: si xifrem dos missatges diferents amb el mateix raig, un XOR entre els dos xifrats elimina el raig i deixa els dos clars combinats entre si, cosa que sol bastar per recuperar-los.
- AES: per què és l'estàndard
AES (Advanced Encryption Standard) és un xifrador de bloc adoptat pel NIST el 2001 després d'un concurs públic internacional de cinc anys en què van participar quinze candidats, amb criptoanàlisi obert de tota la comunitat. Va guanyar l'algorisme belga Rijndael. Aquesta història és la millor il·lustració de Kerckhoffs que existeix: l'algorisme es va fer fort precisament per ser públic i per haver estat atacat durant dècades sense èxit pràctic.
| Característica | Valor |
|---|---|
| Mida de bloc | 128 bits (16 bytes), sempre |
| Mides de clau | 128, 192 o 256 bits |
| Rondes | 10, 12 o 14 segons la clau |
| Estat el 2026 | Sense atacs pràctics. Acceptat per a informació classificada |
| Acceleració per maquinari | Sí: instruccions AES-NI en x86 i equivalents en ARM |
AES-128 davant d'AES-256. Tots dos estan fora de l'abast de qualsevol força bruta (recorda el càlcul de 03-01: 2^128 són onze bilions d'anys amb una màquina impossible). AES-256 es tria quan la dada ha de continuar confidencial durant dècades, quan una norma sectorial ho exigeix o quan es vol marge davant de l'amenaça quàntica a llarg termini. A Nimbus farem servir 256 perquè el cost de rendiment és marginal i la conversa amb auditors se simplifica. ChaCha20 és l'alternativa moderna: un xifrador de flux molt ràpid en programari pur, habitual en dispositius mòbils sense acceleració AES —i per tant rellevant per a l'app de Nimbus—; combinat amb l'autenticador Poly1305 dona ChaCha20-Poly1305, un AEAD de la mateixa categoria que AES-GCM.
El bloc de 128 bits i el problema que crea
AES xifra exactament 16 bytes. Un adjunt d'una clínica pot pesar 3 MB. Com es xifren 3 MB amb una funció que només sap manejar 16 bytes?
La resposta és un mode d'operació: el conjunt de regles que diu com trossejar el missatge, com encadenar els blocs i com farcir l'últim. I aquí hi ha la idea més important de la lliçó:
AES és intatxable; els modes són on la gent s'equivoca. Pràcticament totes les fallades de xifratge simètric que veuràs a la teva carrera són fallades de mode, de nonce o de gestió de clau. Mai del xifrador.
- Modes d'operació: per què un bloc no basta
Per entendre el problema, imagina que xifrem cada bloc de 16 bytes per separat amb la mateixa clau i sense res més. Com que AES és determinista, dos blocs de text en clar idèntics produeixen dos blocs xifrats idèntics. L'estructura de l'original sobreviu al xifratge.
La demostració clàssica és la del pingüí: si es pren la imatge d'un pingüí i es xifra en mode ECB, el resultat continua sent, visiblement, un pingüí. Els colors canvien, però les zones d'un mateix to uniforme es converteixen en zones d'un altre to uniforme, i la silueta es reconeix sense cap esforç. Aquest és el sentit literal de «el xifratge no ha de conservar estructura».
Traslladat a Nimbus: si es xifressin en ECB els registres de cites, totes les files el camp estat de les quals valgués CANCELLADA tindrien el mateix bloc xifrat. Sense desxifrar res, un observador comptaria cancel·lacions per clínica i detectaria patrons. La confidencialitat es perd sense trencar AES.
El que cal és que el mateix text en clar xifrat dues vegades produeixi resultats diferents. Aquest és el paper de l'IV (vector d'inicialització) o del nonce: un valor variable que es combina amb el procés perquè cada xifratge sigui únic.
| Terme | Significat | Requisit |
|---|---|---|
| IV | Valor d'inicialització, en modes com CBC | Impredictible (aleatori) i únic |
| Nonce | Number used once, en CTR i GCM | Únic per clau; no cal que sigui impredictible, però sí irrepetible |
| Etiqueta (tag) | Codi d'autenticació produït per un AEAD | Es verifica en desxifrar; si falla, no es retorna res |
Ni l'IV ni el nonce no són secrets: s'emmagatzemen al costat del text xifrat, en clar. L'únic secret és la clau.
- ECB, CBC i CTR: el catàleg clàssic i els seus paranys
| Mode | Confidencialitat? | Integritat? | Paral·lelitzable? | Risc principal |
|---|---|---|---|---|
| ECB | No de debò | No | Sí | Conserva patrons. Prohibit sempre |
| CBC | Sí, amb IV impredictible | No | Només en desxifrar | Oracle de farciment; IV predictible; mal·leabilitat |
| CTR | Sí | No | Sí (xifrar i desxifrar) | Reutilitzar el nonce és catastròfic |
| GCM (AEAD) | Sí | Sí | Sí | Reutilitzar el nonce trenca a més l'autenticació |
| ChaCha20-Poly1305 (AEAD) | Sí | Sí | Parcialment | Reutilitzar el nonce |
Detall de cadascun:
- ECB (Electronic Codebook). Cada bloc es xifra independentment. És el pingüí. No hi ha cap cas d'ús legítim en un sistema d'informació. Si el trobes al codi, és una troballa d'auditoria, no una preferència d'estil.
- CBC (Cipher Block Chaining). Cada bloc es combina per XOR amb el xifrat de l'anterior abans de xifrar-se; el primer, amb l'IV. Resol els patrons, però arrossega tres inconvenients: necessita farciment —cosa que històricament va obrir els atacs de padding oracle, en què l'atacant desxifra sense la clau observant com respon el servidor a farciments no vàlids—, exigeix un IV impredictible (un de predictible habilita atacs de text en clar triat), i no dona integritat: un atacant pot alterar bits del xifrat i provocar canvis controlats en el clar.
- CTR (Counter). Converteix AES en un xifrador de flux: es xifra un comptador creixent i el resultat es combina per XOR amb les dades. Ràpid, paral·lelitzable i sense farciment. Però si es repeteix el parell (clau, nonce), es repeteix el raig i l'atacant recupera els clars. Tampoc no dona integritat.
La lectura de la taula és contundent: cap mode clàssic no protegeix la integritat. I sense integritat, la confidencialitat és fràgil, perquè un atacant que pot modificar el text xifrat i observar el comportament del sistema té un camí cap a les dades. D'aquí la resposta moderna.
- Xifratge autenticat (AEAD) i dades addicionals autenticades
AEAD significa Authenticated Encryption with Associated Data: xifratge i autenticació en una sola operació i amb una sola clau. En xifrar es produeix, a més del text xifrat, una etiqueta d'autenticació de 16 bytes. En desxifrar, la llibreria verifica l'etiqueta abans de retornar res: si no quadra, llança una excepció i no lliura ni un byte.
Per què això és avui la resposta per defecte:
- Elimina tota una família d'atacs. Sense integritat, existeixen els oracles de farciment, la mal·leabilitat i la manipulació de bits. Amb AEAD, alterar un sol bit del text xifrat produeix un error, no un text en clar diferent.
- Evita l'error de combinar malament. Abans es feia «xifrar i després calcular un MAC», i l'ordre importava: MAC-then-encrypt i encrypt-and-MAC tenen problemes coneguts; només encrypt-then-MAC és correcte. AEAD treu aquesta decisió de les mans del desenvolupador.
- És el que fan servir els protocols seriosos. TLS 1.3 només admet suites AEAD (03-05).
Les dades addicionals autenticades (AAD)
Un AEAD accepta una tercera dada d'entrada: les AAD, que s'autentiquen però no es xifren. Són metadades que viatgen en clar però queden lligades criptogràficament al text xifrat: si algú les canvia, el desxifratge falla.
La seva utilitat a Nimbus és directa. Els adjunts del bucket A-05 són objectes independents amb noms predictibles. Si només xifrem el contingut, un atacant amb permís d'escriptura al bucket podria moure l'objecte xifrat de la clínica CL-014 a la carpeta de la clínica CL-207: el contingut es continuaria desxifrant sense problema, perquè la clau és la mateixa, i la clínica equivocada veuria el document d'una altra. És una fuita entre tenants sense trencar res.
La solució és posar el context a les AAD:
Ara el text xifrat només es desxifra si es presenta amb aquest context exacte. Mogut a un altre tenant, el desxifratge llança una excepció. És la traducció criptogràfica del WHERE tenant_id que va corregir l'IDOR a 01-01: la dada queda lligada al seu amo.
- Xifrar un adjunt de Nimbus amb AES-GCM
Aquest és l'exemple central de la lliçó. Fa servir la llibreria cryptography, que és la implementació de referència en Python i la que has de fer servir en producció.
import secrets
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.exceptions import InvalidTag
# ---------------------------------------------------------------
# 1. LA CLAU. En produccio NO es genera aqui: s obte del gestor
# de secrets o del KMS (03-06). Es genera una sola vegada.
# ---------------------------------------------------------------
clau = AESGCM.generate_key(bit_length=256) # 32 bytes de font segura
aead = AESGCM(clau)
# ---------------------------------------------------------------
# 2. LES DADES I EL SEU CONTEXT
# ---------------------------------------------------------------
adjunt = b"Informe de seguiment. Pacient ficticia: Ana Ruiz. Sessio 7 de 10."
tenant_id = "CL-014"
adjunt_id = "9f31c7"
aad = f"tenant:{tenant_id}|adjunt:{adjunt_id}".encode()
# ---------------------------------------------------------------
# 3. XIFRAR. Nonce de 96 bits (12 bytes), NOU a cada operacio.
# ---------------------------------------------------------------
nonce = secrets.token_bytes(12)
xifrat = aead.encrypt(nonce, adjunt, aad) # retorna ciphertext || etiqueta
# ---------------------------------------------------------------
# 4. EL QUE ES DESA AL BUCKET: nonce + xifrat. El nonce NO es
# secret; sense ell no es pot desxifrar, aixi que es desa al costat.
# ---------------------------------------------------------------
objecte = nonce + xifrat
print(f"Clar: {len(adjunt)} B | Desat: {len(objecte)} B "
f"(+12 nonce +16 etiqueta)")
# ---------------------------------------------------------------
# 5. DESXIFRAR amb el MATEIX context
# ---------------------------------------------------------------
n_llegit, c_llegit = objecte[:12], objecte[12:]
recuperat = aead.decrypt(n_llegit, c_llegit, aad)
print("Desxifrat OK:", recuperat.decode())
# ---------------------------------------------------------------
# 6. QUE PASSA SI ALGU ALTERA UN SOL BYTE DEL TEXT XIFRAT
# ---------------------------------------------------------------
manipulat = bytearray(objecte)
manipulat[20] ^= 0x01 # s inverteix UN bit
try:
aead.decrypt(bytes(manipulat[:12]), bytes(manipulat[12:]), aad)
except InvalidTag:
print("MANIPULACIO DETECTADA: l etiqueta no valida. No es retorna res.")
# ---------------------------------------------------------------
# 7. QUE PASSA SI L OBJECTE ES MOU A UN ALTRE TENANT
# ---------------------------------------------------------------
aad_fals = f"tenant:CL-207|adjunt:{adjunt_id}".encode()
try:
aead.decrypt(n_llegit, c_llegit, aad_fals)
except InvalidTag:
print("CONTEXT INCORRECTE: l adjunt esta lligat a CL-014.")Sortida:
Clar: 65 B | Desat: 93 B (+12 nonce +16 etiqueta)
Desxifrat OK: Informe de seguiment. Pacient ficticia: Ana Ruiz. Sessio 7 de 10.
MANIPULACIO DETECTADA: l etiqueta no valida. No es retorna res.
CONTEXT INCORRECTE: l adjunt esta lligat a CL-014.Punts que cal entendre d'aquest codi, un a un:
AESGCM.generate_key(bit_length=256)fa servir internament el generador segur del sistema. No derivis mai una clau d'unstrfix ni d'una contrasenya ambsha256(...): això es fa amb un KDF (apartat 10 i 03-04).- El nonce és de 12 bytes (96 bits) perquè és la mida per a la qual GCM està especificat i optimitzat. Altres mides obliguen a un processament addicional i augmenten el risc d'error.
secrets.token_bytes(12)dins del flux de xifratge, mai fora. Un nonce calculat una vegada i reutilitzat en un bucle és la fallada més freqüent (apartat 8).encryptretorna el text xifrat amb l'etiqueta ja concatenada al final. Per això el resultat ocupa 16 bytes més que l'original. No has de gestionar l'etiqueta per separat.decryptverifica abans de desxifrar. Si l'etiqueta no quadra llançaInvalidTagi no retorna dades parcials. No capturis mai aquesta excepció per «continuar endavant amb el que hi hagi»: no hi ha res vàlid a recuperar.- L'AAD no es desa xifrat perquè no cal:
tenant_idiadjunt_idja són a la ruta de l'objecte i a la base de dades. El que aporta l'AAD és que no es puguin canviar sense invalidar el xifratge. - La mida es conserva gairebé exactament: 65 bytes de contingut produeixen 93 de desats. GCM no farceix, així que revela la longitud aproximada de l'original. Si aquesta longitud és sensible (per exemple, distingir un informe llarg d'un de curt), s'afegeix farciment explícit abans de xifrar.
L'antipatró: ECB
# =============== INCORRECTE — NO FER SERVIR MAI ===============
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
clau_dolenta = b"0" * 32
xifrador = Cipher(algorithms.AES(clau_dolenta), modes.ECB()).encryptor()
bloc_a = b"ESTAT: CANCELLADA"[:16]
bloc_b = b"ESTAT: CANCELLADA"[:16]
print(xifrador.update(bloc_a).hex())
print(xifrador.update(bloc_b).hex())
# Les dues linies imprimeixen EXACTAMENT el mateix:
# l atacant dedueix que les dues cites tenen el mateix estat sense la clau.
# ==============================================================Tres coses estan malament aquí: el mode ECB (patrons visibles), la clau constant (no és un secret) i l'absència d'integritat. La versió correcta és la del bloc anterior: AES-GCM, clau del gestor de secrets, nonce únic, AAD amb el context.
- La regla del nonce
És la regla operativa més important del xifratge simètric modern:
Amb una mateixa clau, un nonce no es repeteix mai.
Per què en GCM és catastròfic i no simplement dolent:
- Es perd la confidencialitat, igual que en CTR: dos missatges xifrats amb el mateix parell (clau, nonce) comparteixen el raig; el XOR dels xifrats revela el XOR dels clars.
- Es perd l'autenticació, i de manera permanent. GCM construeix la seva etiqueta amb una clau d'autenticació derivada de la principal. Amb dos missatges sota el mateix nonce, un atacant pot recuperar aquesta clau d'autenticació i, a partir d'aquí, falsificar etiquetes vàlides per a missatges que ell triï, indefinidament i per a tots els nonces. No és una fuita puntual: és la pèrdua de la propietat d'integritat per a aquella clau.
Les dues estratègies vàlides per generar nonces:
| Estratègia | Com | Quan fer-la servir | Risc |
|---|---|---|---|
| Aleatori de 96 bits | secrets.token_bytes(12) a cada operació |
Sistemes distribuïts, diversos processos, sense estat compartit | Col·lisió per aniversari. Segur fins a ~2^32 missatges per clau |
| Comptador | Comptador monòton persistent, opcionalment amb prefix d'instància | Un únic emissor amb estat fiable | Un reinici que reposi el comptador a zero repeteix nonces |
Recomanació per a Nimbus: nonce aleatori de 12 bytes, perquè l'API corre en diversos contenidors sense estat compartit i un comptador exigiria coordinació. Amb el límit pràctic de rotar la clau abans d'acostar-se a 2^32 missatges (uns quatre mil milions), que en el volum de Nimbus significa una rotació anual folgada (03-06).
Els tres errors concrets que cal vigilar en revisió de codi:
# INCORRECTE 1: nonce fora del bucle -> es repeteix a cada iteracio
nonce = secrets.token_bytes(12)
for adj in adjunts:
desar(aead.encrypt(nonce, adj, aad)) # tots amb el MATEIX nonce
# INCORRECTE 2: nonce derivat de l identificador -> es repeteix en reeditar
nonce = adjunt_id.encode()[:12]
# INCORRECTE 3: nonce amb random -> predictible (03-01)
nonce = bytes(random.randint(0, 255) for _ in range(12))
# CORRECTE: un de nou, de font segura, dins del bucle
for adj in adjunts:
n = secrets.token_bytes(12)
desar(n + aead.encrypt(n, adj, aad))
- Xifrar una còpia de seguretat amb
openssl
opensslLa Lucía necessita xifrar el bolcat nocturn abans de pujar-lo a l'emmagatzematge extern. Des de la línia d'ordres:
# 1. Generar una clau de 256 bits en hexadecimal (64 caracters hex)
openssl rand -hex 32 > clau_copies.hex
chmod 600 clau_copies.hex
# 2. Xifrar el bolcat
openssl enc -aes-256-cbc \
-pbkdf2 -iter 600000 -md sha256 \
-salt \
-in copia-2026-08-02.tar.gz \
-out copia-2026-08-02.tar.gz.enc \
-pass file:clau_copies.hex
# 3. Desxifrar (prova de restauracio: obligatoria, veure 02-04)
openssl enc -d -aes-256-cbc \
-pbkdf2 -iter 600000 -md sha256 \
-in copia-2026-08-02.tar.gz.enc \
-out copia-restaurada.tar.gz \
-pass file:clau_copies.hexOpció per opció:
| Opció | Què fa | Per què importa |
|---|---|---|
enc |
Subordre de xifratge simètric de fitxers | — |
-aes-256-cbc |
Algorisme i mode | AES de 256 bits en CBC |
-pbkdf2 |
Deriva la clau real amb PBKDF2 en lloc del mètode heretat | Sense això, openssl enc fa servir una derivació antiga basada en MD5 i una sola iteració: inacceptable |
-iter 600000 |
Iteracions de la derivació | Encareix els intents si la contrasenya fos feble (03-04) |
-md sha256 |
Hash de la derivació | Evita l'MD5 heretat |
-salt |
Sal aleatòria per fitxer (per defecte en versions actuals) | Dues còpies iguals donen xifrats diferents |
-pass file:... |
Llegeix el secret d'un fitxer | Mai -pass pass:...: quedaria a l'historial i a ps |
-d |
Desxifrar | — |
Advertiment important sobre aquesta eina.
openssl encprodueix xifratge sense autenticació: CBC no porta etiqueta, així que no detecta manipulació del fitxer xifrat. És acceptable per a una còpia que a més va acompanyada del seu hash verificat (03-04) i viu en un emmagatzematge immutable, però no és el patró recomanat per a dades noves. Per a còpies de seguretat avui convé fer servir eines que facin AEAD per disseny —age,restic,borgamb xifratge, o el xifratge del costat del servidor del proveïdor amb claus gestionades (03-06, 03-07)— en lloc de construir l'esquema a mà. I en tots els casos, la clau no pot viure a la mateixa màquina ni al mateix compte cloud que la còpia: si l'atacant arriba a les còpies, arribaria també a la clau.
- Derivació de claus: HKDF
Un problema pràctic freqüent: Nimbus té una clau mestra al gestor de secrets, però necessita claus diferents per a adjunts, per a còpies i per a l'índex cec de cerca (03-07). Fer servir la mateixa clau per a tot és mala pràctica —viola la separació de dominis i multiplica el risc de col·lisió de nonces—, però desar cinc claus mestres multiplica la feina de custòdia.
La solució és HKDF (HMAC-based Key Derivation Function): deriva múltiples claus independents a partir d'una de sola, de manera que conèixer-ne una de derivada no revela res de la mestra ni de les germanes.
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
clau_mestra = obtenir_del_gestor_de_secrets() # 32 bytes d alta entropia
def derivar(proposit: str) -> bytes:
"""Deriva una clau de 256 bits per a un proposit concret."""
return HKDF(
algorithm=hashes.SHA256(),
length=32, # 32 bytes = 256 bits
salt=None, # opcional; si hi ha sal, millor
info=proposit.encode(), # SEPARACIO DE DOMINIS
).derive(clau_mestra)
k_adjunts = derivar("nimbus/v1/adjunts")
k_copies = derivar("nimbus/v1/copies")
k_index = derivar("nimbus/v1/index-cec")Claus d'aquest codi:
infoés l'etiqueta de propòsit. Canviar una sola lletra produeix una clau completament diferent: és el que garanteix que les tres derivades siguin independents. Incloure la versió (v1) permet rotar sense ambigüitat (03-06).length=32fixa la mida de sortida. HKDF pot produir qualsevol longitud a partir de la mateixa entrada.- La clau mestra ha de tenir alta entropia ja. HKDF expandeix i separa, no enforteix. No serveix per derivar d'una contrasenya humana.
La distinció que no has de confondre: HKDF deriva claus a partir d'altres claus i és deliberadament ràpid. Derivar a partir de contrasenyes humanes requereix una funció deliberadament lenta —Argon2id, scrypt, bcrypt, PBKDF2—, perquè l'entrada té poca entropia i cal encarir cada intent de l'atacant. Això s'estudia a 03-04.
- Rendiment i les seves conseqüències de disseny
Els ordres de magnitud expliquen per què els sistemes reals estan construïts com estan. Xifres orientatives en una CPU de servidor moderna amb AES-NI:
| Operació | Rendiment aproximat | Relació |
|---|---|---|
| AES-256-GCM (xifrar/desxifrar) | ~1–5 GB/s per nucli | Referència |
| ChaCha20-Poly1305 | ~1–2 GB/s (millor sense AES-NI) | Comparable |
| SHA-256 | ~1–2 GB/s | Comparable |
| Signatura Ed25519 | ~desenes de milers/s | Molt inferior en volum |
| RSA-2048, operació privada | ~1.000–2.000/s | ~10.000 vegades més lent per byte |
| RSA-4096, operació privada | ~150–300/s | Prohibitiu per a dades |
Les tres conseqüències de disseny: (1) mai no es xifren dades grans amb criptografia asimètrica —RSA ni tan sols pot xifrar més bytes que el seu mòdul—, d'on neix el xifratge híbrid de 03-03; (2) xifrar en repòs és pràcticament gratis, així que «alenteix la base de dades» va deixar de ser un argument acceptable fa quinze anys; i (3) el cost és a les operacions asimètriques per connexió, no als bytes, raó per la qual TLS reutilitza sessions i una API amb moltes connexions curtes gasta més CPU en encaixades que en xifrar trànsit (03-05).
- El problema que queda obert: distribuir la clau
Tot l'anterior funciona si les dues parts ja comparteixen la clau. I aquí és on el simètric xoca contra el seu límit. Pensa en el cas més quotidià de Nimbus:
Una fisioterapeuta obre l'app al mòbil, a la sala d'espera, connectada a la wifi de la clínica. El seu dispositiu i l'API de Nimbus no s'han vist mai abans. Necessiten una clau simètrica comuna per xifrar la sessió. Com se la comuniquen, si l'únic canal que tenen és precisament el que volen protegir?
Enviar-la pel canal no serveix: qui escolta la captura. Repartir-la per endavant tampoc: els clients són milers i canvien a diari. I el problema d'escala és demolidor: amb 10.000 usuaris finals caldrien gairebé cinquanta milions de claus. Aquest és el problema de la distribució de claus, que va estar sense resoldre durant els primers quatre mil anys d'història de la criptografia; es va resoldre als anys setanta, i la seva solució és la raó per la qual existeix el comerç a internet.
Errors Comuns i Consells
Errors de mode i paràmetres
- Fer servir ECB. Sense excepcions ni matisos. Si apareix al codi, és una troballa.
- Triar CBC per a codi nou. No està trencat, però exigeix farciment correcte, IV impredictible i un MAC afegit bé. L'AEAD t'estalvia les tres decisions.
- Reutilitzar el nonce. La fallada més greu i més comuna. Nonce nou dins del bucle, sempre, amb
secrets. - Creure que xifrar dona integritat. Només la dona l'AEAD.
- Capturar
InvalidTagi continuar. Aquesta excepció significa «aquestes dades han estat alterades o el context és incorrecte». Es registra l'esdeveniment i s'avorta.
Errors de clau
- Derivar la clau amb
sha256(contrasenya). No és un KDF. Contrasenyes: Argon2id (03-04). Claus a partir de claus: HKDF. - Clau escrita al codi o en un
.envversionat. És exactament la fallada que va permetre l'escalada en el ransomware de 02-06 i es tracta a 03-06. - Una única clau per a tot el sistema, o desar-la al costat de la dada xifrada. Deriva per propòsit amb HKDF, i recorda que una còpia xifrada amb la seva clau al mateix bucket és xifratge decoratiu.
Consells
- La resposta per defecte d'aquest curs per a xifratge simètric nou és: AES-256-GCM (o ChaCha20-Poly1305 sense acceleració per maquinari), nonce aleatori de 12 bytes, AAD amb el context de negoci, clau del gestor de secrets.
- Desa l'objecte xifrat com a
versió || nonce || ciphertext+etiqueta. Aquest primer byte de versió et permetrà canviar d'algorisme d'aquí a tres anys sense reescriure les dades antigues. - Lliga el xifratge al context amb AAD sempre que la dada tingui amo: en un SaaS multi-tenant, això és sempre. Xifrar és barat; dissenyar on viu la clau és la feina de debò.
Exercicis
Exercici 1 — Diagnosticar una implementació
L'Iván ha escrit aquesta funció per xifrar les notes internes de les reserves:
import random
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
CLAU = b"NimbusReservas2026SecretKey12345" # (1)
IV = b"0123456789abcdef" # (2)
def xifrar_nota(nota: str) -> bytes:
farciment = 16 - (len(nota) % 16)
nota += chr(farciment) * farciment
c = Cipher(algorithms.AES(CLAU), modes.CBC(IV)).encryptor() # (3)
return c.update(nota.encode()) + c.finalize()
def id_operacio() -> str:
return "".join(random.choice("abcdef0123456789") for _ in range(16)) # (4)Es demana: (a) enumera els quatre problemes marcats i explica l'atac concret que habilita cadascun; (b) indica quina propietat de seguretat falta per complet i quina conseqüència té; (c) reescriu la funció amb AES-GCM, incloent-hi l'AAD adequat per a Nimbus i el format d'emmagatzematge; (d) explica què cal fer amb les notes ja xifrades amb el codi antic.
Exercici 2 — Triar mode i paràmetres
Per a cada cas de Nimbus, decideix: algorisme i mode, mida de clau, com es genera el nonce/IV, què poses a l'AAD (o per què no escau) i on viu la clau.
- Adjunts de clíniques al bucket A-05, fins a 20 MB, milers al dia, diversos contenidors d'API en paral·lel.
- Bolcat nocturn de PostgreSQL, 8 GB, un sol procés, restauració possible d'aquí a cinc anys.
- Camp
notes_cliniquesde la taula de cites, uns 500 caràcters, xifrat a nivell d'aplicació. - Contingut de la sessió entre l'app mòbil i l'API.
- Fitxer de configuració amb credencials de tercers que es desplega amb els contenidors.
Exercici 3 — Explicar el nonce a la direcció
La Marta ha llegit que «una fallada de nonce» va tombar una altra empresa i et demana una explicació de tres paràgrafs, sense matemàtiques, per al comitè.
Es demana: (a) explica amb una analogia què és un nonce i per què ha de ser únic; (b) explica per què repetir-lo en GCM és pitjor que repetir-lo en CTR; (c) digues quins dos controls concrets implantaries a Nimbus perquè aquesta fallada no pugui passar, i qui és responsable de cadascun.
Solucions
Exercici 1
(a) Els quatre problemes:
| # | Problema | Atac que habilita |
|---|---|---|
| 1 | Clau escrita al codi, i a més derivada d'una frase llegible | Qualsevol amb accés al repositori, a una còpia del codi o al contenidor té la clau. És el patró del .env de 02-06. A més no és entropia real: són caràcters ASCII |
| 2 | IV constant | Amb IV fix, CBC torna a ser determinista per al primer bloc: dues notes amb el mateix començament donen el mateix xifrat inicial. Permet correlacionar i habilita text en clar triat |
| 3 | CBC sense autenticació i amb farciment artesanal | Mal·leabilitat (alterar bits del xifrat altera el clar de manera predictible) i risc d'oracle de farciment si el servidor distingeix errors |
| 4 | random per a un identificador |
Predictible (03-01). Si aquest identificador es fa servir en URL o referències, és enumerable |
(b) Falta la integritat/autenticitat. Conseqüència: ningú no detecta que una nota xifrada ha estat substituïda o modificada; el sistema desxifrarà brossa o text manipulat i ho mostrarà com a legítim.
(c) Versió correcta:
import secrets
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
VERSIO = b"\x01"
def xifrar_nota(nota: str, tenant_id: str, reserva_id: int, clau: bytes) -> bytes:
aead = AESGCM(clau) # clau del gestor (03-06)
nonce = secrets.token_bytes(12) # NOU a cada crida
aad = f"v1|tenant:{tenant_id}|reserva:{reserva_id}".encode()
xifrat = aead.encrypt(nonce, nota.encode("utf-8"), aad)
return VERSIO + nonce + xifrat # format versionat
def desxifrar_nota(blob: bytes, tenant_id: str, reserva_id: int, clau: bytes) -> str:
assert blob[:1] == VERSIO, "versio de xifratge desconeguda"
nonce, xifrat = blob[1:13], blob[13:]
aad = f"v1|tenant:{tenant_id}|reserva:{reserva_id}".encode()
return AESGCM(clau).decrypt(nonce, xifrat, aad).decode("utf-8")(d) Les notes antigues. No n'hi ha prou de canviar el codi: (1) es considera la clau antiga compromesa des del moment en què va estar al repositori, així que es rota; (2) s'executa una migració que desxifra amb l'esquema antic i torna a xifrar amb el nou, tenant a tenant; (3) el byte de versió permet que tots dos formats convisquin durant la migració; (4) s'elimina la clau antiga de l'historial del repositori sabent que esborrar el commit no revoca res (03-06); (5) es registra l'incident i s'avalua si hi va haver accés indegut.
Exercici 2
| Cas | Algorisme/mode | Clau | Nonce/IV | AAD | On viu la clau |
|---|---|---|---|---|---|
| 1. Adjunts | AES-256-GCM (o xifratge per trossos per a arxius grans) | 256 bits derivada amb HKDF info="adjunts" |
Aleatori 12 B per objecte (diversos contenidors: no hi cap comptador) | tenant_id, adjunt_id, versió |
KMS/gestor de secrets; clau de dades per objecte amb xifratge de sobre (03-06) |
| 2. Bolcat | AEAD mitjançant eina dedicada (age, restic) millor que openssl enc |
256 bits | La gestiona l'eina | Identificador de la còpia i data | Fora del compte cloud de producció. Còpia en custòdia fora de línia |
3. Camp notes_cliniques |
AES-256-GCM a nivell d'aplicació | Derivada amb HKDF info="notes" |
Aleatori 12 B per escriptura | tenant_id, cita_id, versio |
KMS. Vegeu 03-07 i el problema de la cerca |
| 4. Sessió app-API | No ho implementis tu: és TLS 1.3 amb AES-GCM o ChaCha20-Poly1305 | La negocia el protocol | El protocol | El protocol | Efímera, en memòria (03-05) |
| 5. Configuració amb credencials | No xifris un fitxer de secrets: no el tinguis. Injecció des del gestor de secrets a l'arrencada | — | — | — | Gestor de secrets (03-06) |
Nota sobre el cas 3: si aquestes notes contenen informació de salut, el disseny s'ha de validar amb el responsable de protecció de dades abans d'implantar-lo; el marc legal es tracta a 06-03.
Exercici 3
(a) Analogia. Un nonce és com el número d'una habitació d'hotel combinat amb la clau mestra: la clau és sempre la mateixa, però obre una habitació diferent cada vegada. Si dos hostes reben el mateix número, acaben a la mateixa habitació i veuen les coses de l'altre. El nonce és el que fa que el mateix secret produeixi un resultat diferent a cada ús; si es repeteix, es repeteix el resultat i apareixen relacions entre missatges que no haurien d'existir.
(b) Per què en GCM és pitjor. En CTR, repetir el nonce trenca la confidencialitat d'aquests dos missatges: és un dany acotat. En GCM, a més, permet a l'atacant deduir el valor intern amb què es calculen les etiquetes d'autenticació i, amb ell, fabricar missatges falsos que el sistema acceptarà com a autèntics, per sempre i amb qualsevol nonce. Es passa de «dos missatges llegits» a «qualsevol pot signar en el teu nom amb aquella clau».
(c) Dos controls.
- Tècnic: una única funció interna de xifratge a la base de codi —una
caixa_forta.xifrar(dades, context)— que generi el nonce internament i no accepti que se li passi des de fora. Si el paràmetre no existeix, no es pot reutilitzar. Responsable: l'Iván, amb revisió de la Marta. - De procés: regla de revisió obligatòria de dues persones per a qualsevol canvi que toqui aquest mòdul, més una comprovació automàtica al CI (02-04) que rebutgi
Cipher(,modes.ECB,random.i nonces literals fora de la caixa forta. Responsable: la Lucía (pipeline), amb la Marta com a aprovadora.
Conclusió
Ja saps fer servir la meitat ràpida de la criptografia. Tens clar què és un esquema simètric —una clau que xifra i desxifra—, quan és la resposta natural a Nimbus (adjunts, còpies, xifratge en repòs, contingut de sessió) i quines són les seves dues virtuts, velocitat i claus curtes. Distingeixes xifratge de bloc de xifratge de flux, i saps per què AES és l'estàndard: un concurs públic, vint-i-cinc anys de criptoanàlisi obert i acceleració per maquinari; la millor demostració pràctica del principi de Kerckhoffs.
Però la lliçó que has de treure'n és la del mode: AES és intatxable i tot i així ECB deixa veure el pingüí, CBC exigeix IV impredictible i farciment correcte, i CTR es desmorona si repeteixes el nonce. Cap dels tres no protegeix la integritat, i per això la resposta per defecte d'avui és el xifratge autenticat: AES-GCM o ChaCha20-Poly1305, amb etiqueta verificada abans de retornar un sol byte, i amb AAD per lligar el text xifrat al seu context —el tenant_id de Nimbus—, que és la versió criptogràfica del WHERE tenant_id amb què es va corregir l'IDOR. Ho has vist funcionar: alterar un bit produeix InvalidTag, i moure un adjunt de CL-014 a CL-207 també. Te'n portes a més la regla més important de l'apartat, un nonce no es repeteix mai sota la mateixa clau, amb el perquè exacte que en GCM això no sigui una fallada puntual sinó la pèrdua permanent de l'autenticació; la pràctica amb openssl enc i el seu advertiment —xifra però no autentica, i la clau no viu mai al costat de la còpia—; HKDF per derivar claus per propòsit a partir d'una de mestra; i els ordres de magnitud que expliquen per què mai no es xifren dades grans amb criptografia asimètrica.
I et queda un problema sense resoldre, el mateix que va tenir la humanitat durant quatre mil anys: si l'únic canal entre l'app mòbil de la fisioterapeuta i l'API de Nimbus és el que volem protegir, com acorden la clau? No hi ha resposta dins del món simètric.
A Criptografia Asimètrica (03-03) veuràs la idea que ho va canviar tot: dues claus diferents i relacionades matemàticament, una de pública que es reparteix sense por i una de privada que no surt mai. Amb ella es resol la distribució mitjançant l'intercanvi Diffie-Hellman i la seva propietat més valuosa, la confidencialitat persistent (forward secrecy); apareix el xifratge híbrid, que és el patró real de TLS; i arriba la signatura digital, l'única primitiva capaç de donar no-repudi. Al final quedarà oberta una altra pregunta, la que donarà sentit a la PKI: aquesta clau pública, de qui és realment?
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
