A la lliçó anterior vas resoldre la confiança: un certificat X.509 lliga la clau pública de portal.medinube.example a la seva identitat amb la signatura d'una CA. Però el certificat, en un fitxer, no protegeix cap byte. Falta el protocol que el fa servir a cada connexió: TLS (Transport Layer Security), la capa que hi ha sota la "s" d'HTTPS. Aquesta lliçó té una cosa especial: no aprendràs gairebé cap primitiva nova. Veuràs com ECDH amb X25519 (04-04), els certificats i la seva cadena (05-01), les signatures (04-03), HKDF (02-04) i els AEAD (02-03) encaixen, peça a peça, al handshake de TLS 1.3 — el moment "ja ho sabies tot" del curs. Després baixarem al fang: inspeccionar servidors amb openssl s_client, fer TLS bé des de Python (i corregir un verify=False heretat a MediNube), configurar nginx com a terminador TLS, i entendre mTLS i el contrast amb SSH.
Contingut
- Què garanteix TLS i on viu
- El handshake de TLS 1.3, peça a peça (ja les coneixes totes)
- Forward secrecy a TLS
- TLS 1.2 vs 1.3 i les versions mortes
- Inspecció pràctica amb
openssl s_client - TLS correcte des de Python (i el pecat de
verify=False) - El costat servidor: nginx com a terminador TLS
- mTLS: quan el client també s'identifica
- SSH, l'altre protocol quotidià: TOFU vs PKI
Què garanteix TLS i on viu
TLS és un protocol que converteix un canal insegur (TCP) en un que ofereix, d'una tirada, tres dels quatre objectius del mòdul 1:
- Confidencialitat: ningú que escolti el cable llegeix els historials mèdics d'Ana Pérez.
- Integritat: ningú modifica les dades en trànsit sense que es detecti (el tag de l'AEAD, 02-03).
- Autenticitat del servidor: la Clínica Sol parla amb el veritable
portal.medinube.example, no amb MalloryClinic (el certificat, 05-01). La del client és opcional (mTLS, més avall).
(El no repudi no és a la llista: TLS fa servir MAC i claus simètriques compartides per al trànsit, així que qualsevol dels dos extrems podria haver generat qualsevol registre. Per a no repudi, signatures d'aplicació com les receptes de 04-03.)
On viu? Entre el transport i l'aplicació:
| Capa | Protocol | Exemple |
|---|---|---|
| Aplicació | HTTP, SMTP, IMAP... | GET /pacients/ana.perez |
| Seguretat | TLS | handshake + registres xifrats |
| Transport | TCP | ports, retransmissió |
| Xarxa | IP | encaminament |
HTTPS no és un protocol diferent d'HTTP: és HTTP corrent sobre TLS (port 443 en lloc de 80). El mateix val per a SMTPS, IMAPS, etc. L'aplicació escriu bytes en clar i TLS els xifra per sota; per això afegir TLS no obliga a reescriure l'aplicació. Important per a MediNube: TLS protegeix dades en trànsit; les dades en repòs (la base de dades d'historials) són un altre problema, que retrobarem a 06-02.
El handshake de TLS 1.3, peça a peça (ja les coneixes totes)
Abans de xifrar el primer byte d'aplicació, client i servidor negocien paràmetres, s'autentiquen i acorden claus. És el handshake. A TLS 1.3 (RFC 8446, 2018) costa una sola anada i tornada (1-RTT):
sequenceDiagram
participant C as Client (navegador d'Ana Pérez)
participant S as portal.medinube.example
Note over C: Genera parell efímer X25519 (com a 04-04)
C->>S: ClientHello<br/>versions + xifrats suportats +<br/>clau pública efímera X25519 + SNI
Note over S: Genera EL SEU parell efímer X25519<br/>ECDH → secret compartit<br/>HKDF → claus de sessió (02-04)
S->>C: ServerHello (clau pública efímera X25519)<br/>🔒 Certificat (cadena de 05-01)<br/>🔒 CertificateVerify (signatura, 04-03)<br/>🔒 Finished
Note over C: ECDH + HKDF → mateixes claus<br/>Verifica la cadena del certificat (05-01)<br/>Verifica la signatura CertificateVerify
C->>S: 🔒 Finished
Note over C,S: 🔒 Dades d'aplicació amb AEAD:<br/>AES-256-GCM o ChaCha20-Poly1305 (02-03)
(🔒 = ja xifrat amb les claus acabades de derivar.) Recorrem cada peça reconeixent d'on ve:
- ClientHello. El client proposa versions i cipher suites, i inclou directament la seva clau pública efímera X25519 (a TLS 1.3 no s'espera a negociar: s'aposta per les corbes més habituals i s'adjunta la key share). També envia l'SNI (Server Name Indication): a quin domini vol connectar-se, imprescindible quan una IP serveix diversos dominis — és el
-servernameque vas fer servir a 05-01. - ECDH efímer (ECDHE). El servidor genera el seu propi parell efímer, i tots dos calculen el secret compartit. Això és, literalment, l'intercanvi X25519 que vas programar a 04-04. La "E" final d'ECDHE significa ephemeral: claus d'usar i llençar, una parella nova per connexió.
- Derivació de claus. Del secret ECDH no es fa servir ni un byte directament: es passa per HKDF (02-04) en un "calendari de claus" (key schedule) que deriva claus diferents per a cada direcció (client→servidor i servidor→client) i cada fase. Regla que ja coneixies: mai facis servir un secret DH cru, deriva'l.
- Certificate. El servidor envia el seu certificat fulla més les intermèdies. El client valida la cadena exactament com a 05-01: signatures fins a una arrel del trust store, dates, i el domini de l'SNI al SAN. Aquí és on mor MalloryClinic.
- CertificateVerify. Subtilesa crucial: el certificat és públic — Mallory podria reenviar el certificat legítim de MediNube. Per això el servidor a més signa amb la seva clau privada una transcripció de tot el handshake (amb ECDSA o RSA-PSS, les signatures de 04-03). Només qui posseeix la clau privada corresponent al certificat pot produir aquesta signatura, i com que cobreix la transcripció (incloses les key shares), tampoc no es pot retallar i enganxar en una altra connexió.
- Finished (tots dos). Un MAC (mòdul 3) sobre la transcripció amb les claus derivades: confirma que tots dos veuen el mateix handshake, sense manipulacions.
- Dades d'aplicació. Tot el trànsit va amb AEAD: AES-256-GCM o ChaCha20-Poly1305 (02-03), amb nonces derivats d'un comptador de registres — irrepetibles per construcció, com exigia la lliçó 02-02.
Fes l'inventari: X25519+ECDH (04-04), HKDF (02-04), certificats i cadena (05-01), signatures RSA-PSS/ECDSA (04-03), MAC (03), AEAD (02-03), CSPRNG per a totes les claus efímeres (01). TLS 1.3 és tot el teu curs, muntat per enginyers de protocols. No hi ha màgia: hi ha composició acurada de peces que ja domines.
Forward secrecy a TLS
A 04-04 vas veure que fer servir claus DH efímeres dona forward secrecy: comprometre les claus a llarg termini no desxifra el trànsit passat. A TLS 1.3 això no és opcional: totes les suites fan servir ECDHE. La clau privada del certificat de portal.medinube.example només es fa servir per signar (CertificateVerify), mai per xifrar trànsit ni transportar claus de sessió.
Conseqüència pràctica que ha d'entendre tot l'equip de MediNube: si demà es filtra la clau privada del certificat (avís per endavant: a 05-03 simularem exactament aquest incident), l'atacant podrà suplantar el portal fins que es revoqui el certificat, però no podrà desxifrar ni un byte del trànsit ja capturat. En TLS 1.2 antic amb suites RSA de transport de clau (sense DHE/ECDHE), sí que podria: gravava trànsit avui i el desxifrava el dia que aconseguís la clau. És una de les grans raons de la taula següent.
TLS 1.2 vs 1.3 i les versions mortes
| Aspecte | TLS 1.2 (2008) | TLS 1.3 (2018) |
|---|---|---|
| Anades i tornades del handshake | 2-RTT | 1-RTT (més ràpid; existeix 0-RTT opcional, amb matisos de replay) |
| Forward secrecy | Opcional (segons suite) | Obligatòria (sempre ECDHE) |
| Xifrats admesos | Desenes de suites, incloses de dolentes: CBC amb MAC-then-encrypt, RC4, 3DES, exportables | Només 5 suites, totes AEAD (AES-GCM, ChaCha20-Poly1305) |
| RSA com a transport de clau | Permès | Eliminat |
| Quant handshake va xifrat | Gairebé res (certificat en clar) | Tot des del ServerHello (el certificat viatja xifrat) |
| Algorismes trencats (MD5, SHA-1, grups DH febles) | Presents a l'ecosistema | Purgats |
| Renegociació i compressió (fonts de CVE històrics) | Presents | Eliminades |
TLS 1.3 és, sobretot, un exercici de poda: treure tot el que dues dècades d'atacs (BEAST, POODLE, Lucky13, FREAK, Logjam...) van demostrar fràgil. Menys opcions = menys combinacions insegures que un administrador pugui triar per error. Això també és criptoagilitat (regla núm. 8): el protocol negocia versió i suite, i per això la migració va ser possible.
Versions mortes — no negociables el 2026: SSLv2 i SSLv3 (POODLE, 2014), TLS 1.0 i TLS 1.1 (deprecades formalment per l'RFC 8996 el 2021; els navegadors les van retirar el 2020). La política de MediNube, que aplicarem a nginx més avall: mínim TLS 1.2, preferit TLS 1.3. TLS 1.2 ben configurat (només suites ECDHE+AEAD) continua sent acceptable; tot el que sigui anterior, no.
Inspecció pràctica amb openssl s_client
openssl s_client és el navegador dels pobres i l'eina de diagnòstic dels professionals: obre una connexió TLS i t'explica tot el que ha negociat.
# Connexió bàsica; -servername envia l'SNI (gairebé sempre necessari);
# -brief resumeix; sense ell tindràs el bolcat complet.
openssl s_client -connect portal.medinube.example:443 \
-servername portal.medinube.example </dev/nullA la sortida (completa) aprèn a localitzar aquests blocs:
CONNECTED(00000003) depth=2 C=US, O=..., CN=Arrel Confiable ← la cadena, des de l'arrel (depth=2) depth=1 C=US, O=..., CN=CA Intermèdia R3 ← fins a la intermèdia (depth=1) depth=0 CN=portal.medinube.example ← i la fulla (depth=0) verify return:1 ← 1 = aquesta baula és vàlida --- Certificate chain 0 s:CN=portal.medinube.example ← s: subject de cada baula i:C=US, O=..., CN=CA Intermèdia R3 ← i: el seu issuer (ha d'encadenar!) 1 s:C=US, O=..., CN=CA Intermèdia R3 i:C=US, O=..., CN=Arrel Confiable --- SSL handshake has read 4321 bytes ... New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 ← protocol i suite negociats Server public key is 256 bit ← clau del certificat (P-256) Verify return code: 0 (ok) ← EL veredicte final ---
Les tres línies d'or: Verify return code: 0 (ok) (la cadena és vàlida contra el trust store; qualsevol altre codi és un problema — 18 = autosignat, 10 = caducat, 20 = falta la intermèdia...), New, TLSv1.3, Cipher is ... (quina versió i suite s'ha negociat) i la secció Certificate chain (què ha enviat el servidor; un clàssic de producció és oblidar configurar la intermèdia i que els clients estrictes fallin).
Trucs de diagnòstic habituals:
# Accepta el servidor versions velles? (hauria de FALLAR):
openssl s_client -connect portal.medinube.example:443 -tls1_1 </dev/null
# Suporta TLS 1.3?
openssl s_client -connect portal.medinube.example:443 -tls1_3 </dev/null
# Provar el TEU laboratori de 05-01 validant contra la TEVA arrel:
openssl s_client -connect localhost:8443 -servername portal.medinube.example \
-CAfile ~/medinube-lab/pki/ca.crt </dev/null
# Veure dates del certificat remot d'un cop (patró de 05-01):
openssl s_client -connect portal.medinube.example:443 \
-servername portal.medinube.example </dev/null 2>/dev/null \
| openssl x509 -noout -datesTLS correcte des de Python (i el pecat de verify=False)
Primer dia revisant integracions a MediNube i apareix aquesta joia al codi heretat que crida l'API del laboratori extern:
import requests
def cridar_api_MALAMENT(url_laboratori: str, dades: dict) -> dict:
# ⚠️ CODI HERETAT — NO COPIAR ⚠️
# "Fix" de 2023: donava SSLError: certificate verify failed,
# i amb això 'es va arreglar'. (Narrador: no es va arreglar.)
resposta = requests.post(
url_laboratori,
json=dades,
verify=False, # ← EL PECAT CAPITAL
timeout=10,
)
return resposta.json()Què fa verify=False? Desactiva completament la validació del certificat: s'accepta qualsevol certificat, de qualsevol, per a qualsevol nom. El trànsit continua xifrat, i aquesta és la trampa psicològica — "surt per HTTPS, quin problema hi ha?". El problema és que està xifrat cap a no se sap qui: MalloryClinic pot presentar el seu certificat casolà de l'exercici 3 de 05-01 i aquest codi l'abraça. És, exactament, desfer tot el mòdul 5 amb un argument de teclat. El SSLError original era el sistema funcionant: probablement faltava la intermèdia al servidor del laboratori, o era un certificat intern la CA del qual calia distribuir. La versió correcta:
import requests
def cridar_api(url_laboratori: str, dades: dict) -> dict:
"""Crida l'API del laboratori amb TLS verificat.
verify=True és el valor per defecte de requests: valida cadena,
dates i nom (SAN) contra el paquet de CA (certifi).
"""
resposta = requests.post(url_laboratori, json=dades, timeout=10)
resposta.raise_for_status()
return resposta.json()
def cridar_api_interna(url: str, dades: dict) -> dict:
"""Variant per a serveis interns signats per la CA del laboratori
de MediNube (05-01): verify= pot apuntar a UNA CA concreta.
Així confiem en la nostra arrel interna NOMÉS per a aquesta crida,
sense tocar el trust store del sistema.
"""
resposta = requests.post(
url, json=dades, timeout=10,
verify="/etc/medinube/pki/ca.crt", # l'arrel del lab de 05-01
)
resposta.raise_for_status()
return resposta.json()Regles: mai verify=False (ni tan sols "temporalment": els temporals de 2023 continuen en producció el 2026); si el certificat és d'una CA interna, passa la ruta d'aquesta CA a verify=; si falla la validació contra un tercer, arregla la causa (avisa el proveïdor que la seva cadena està incompleta).
Un esglaó per sota de requests, la stdlib:
import socket
import ssl
# create_default_context() és LA manera correcta: activa validació de
# certificat I de nom, carrega les CA del sistema i fixa versions
# mínimes segures. No construeixis SSLContext() a pèl.
context = ssl.create_default_context()
# Per a serveis interns del laboratori: afegeix la teva arrel al context.
# context.load_verify_locations("/etc/medinube/pki/ca.crt")
with socket.create_connection(("portal.medinube.example", 443)) as tcp:
# server_hostname fa doble servei: envia l'SNI i activa
# la comprovació del nom contra el SAN.
with context.wrap_socket(tcp, server_hostname="portal.medinube.example") as tls:
print("Protocol:", tls.version()) # p. ex. 'TLSv1.3'
print("Suite:", tls.cipher()[0]) # p. ex. 'TLS_AES_256_GCM_SHA384'
cert = tls.getpeercert() # dict amb subject, SAN, dates
print("SAN:", cert.get("subjectAltName"))Els equivalents del pecat capital en aquest nivell, que trobaràs en més codi heretat: ssl._create_unverified_context(), context.check_hostname = False, context.verify_mode = ssl.CERT_NONE. Tots són verify=False amb una altra disfressa; tots han de morir en revisió de codi.
El costat servidor: nginx com a terminador TLS
L'aplicació Python de MediNube no parla TLS directament: al davant es posa nginx com a terminador TLS (rep HTTPS del món, desxifra, i passa HTTP a l'aplicació per xarxa interna). Avantatges: la configuració TLS viu en un sol lloc, mantinguda per gent que només hi pensa, i l'aplicació queda simple. Configuració essencial comentada:
# /etc/nginx/sites-available/portal.medinube.example
# Bloc 1: el port 80 NOMÉS redirigeix a HTTPS. Res de servir contingut
# en clar "perquè és només la portada".
server {
listen 80;
server_name portal.medinube.example;
return 301 https://$host$request_uri;
}
# Bloc 2: el servei real, només per TLS.
server {
listen 443 ssl;
http2 on;
server_name portal.medinube.example;
# Certificat: fullchain = fulla + intermèdies (recorda l'error
# clàssic vist a s_client!). privkey = la clau privada, permisos 600.
ssl_certificate /etc/medinube/tls/fullchain.pem;
ssl_certificate_key /etc/medinube/tls/privkey.pem;
# Política de versions de MediNube: 1.2 mínim, 1.3 preferit.
ssl_protocols TLSv1.2 TLSv1.3;
# Per a TLS 1.2, només suites ECDHE+AEAD (a 1.3 no cal: totes ho són).
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305;
# HSTS: diu al navegador "durant 2 anys, parla'm NOMÉS per HTTPS".
# Tanca la finestra del primer accés per HTTP. Menció breu: activa'l
# quan estiguis segur; amb 'preload' és gairebé irreversible.
add_header Strict-Transport-Security "max-age=63072000" always;
location / {
proxy_pass http://127.0.0.1:8000; # l'app Python, a xarxa interna
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
}
}No memoritzis suites: fes servir el generador de configuracions de Mozilla (perfil "intermediate") i les eines de test (SSL Labs, testssl.sh) per verificar el resultat. El que importa és el què: redirecció 80→443, cadena completa, versions mínimes, HSTS. Com aconsegueix MediNube aquest fullchain.pem i com es renova sol, a la propera lliçó (05-03).
mTLS: quan el client també s'identifica
Fins ara, TLS autentica el servidor; el client (Ana Pérez) s'autentica després, dins de l'aplicació, amb la seva contrasenya Argon2id del mòdul 3. Però per a les integracions màquina a màquina — el servidor de la Clínica Sol o del Centre Mèdic Luna cridant l'API de MediNube — hi ha una opció millor: mTLS (TLS mutu), on també el client presenta un certificat i signa el seu CertificateVerify. El handshake és el mateix amb un pas extra: el servidor sol·licita certificat (CertificateRequest) i el client respon amb el seu.
Quan fer-lo servir? Servei-a-servei amb poques parts conegudes: integracions B2B com les clíniques, microserveis interns, agents de monitorització. Quan no? Usuaris humans finals (gestionar certificats al navegador de cada pacient és inviable; aquí, contrasenyes+MFA o passkeys).
Disseny de MediNube: els certificats de client de les clíniques els emet la CA interna del laboratori de 05-01, amb SAN clinica-sol.clientes.medinube.example i centro-luna.clientes.medinube.example. A nginx, el costat servidor s'activa així:
# Dins del server 443 de l'API d'integracions:
ssl_client_certificate /etc/medinube/pki/ca.crt; # CA que emet els certs de clínica
ssl_verify_client on; # exigeix certificat de client
# L'app rep la identitat verificada, sense gestionar contrasenyes d'API:
proxy_set_header X-Clinica-DN $ssl_client_s_dn;I el client, des de Python: requests.post(url, cert=("/etc/clinica-sol/tls/client.crt", "/etc/clinica-sol/tls/client.key"), verify="/etc/clinica-sol/tls/medinube-ca.crt"). Fixa't en la simetria: cada costat valida l'altre contra una CA; MalloryClinic no pot suplantar cap dels dos extrems. El preu és operatiu: emetre, renovar i sobretot revocar certificats de clínica quan una es dona de baixa — exactament el cicle de vida de 05-03.
SSH, l'altre protocol quotidià: TOFU vs PKI
Tanca el mapa mental amb l'altre protocol segur que fas servir cada dia: SSH. Criptogràficament és un cosí germà de TLS (intercanvi de claus amb Curve25519, AEAD, signatures Ed25519 — les teves peces una altra vegada!), però resol la confiança amb un model diferent:
| TLS (Web PKI) | SSH (típic) | |
|---|---|---|
| Model de confiança | Tercers de confiança (CA) | TOFU: Trust On First Use |
| Primera connexió | Cadena validable des del minut zero | Et mostra l'empremta de la clau del servidor i et pregunta ("Are you sure you want to continue connecting?") |
| Connexions següents | Es revalida la cadena cada vegada | Compara contra ~/.ssh/known_hosts; si la clau ha canviat, alarma |
| Punt feble | La pitjor CA del trust store | La primera connexió: si Mallory és al mig aquell dia, has acceptat la seva clau |
| Escala | Milions de servidors desconeguts | Un grapat de servidors que administres |
TOFU és raonable quan connectes poques vegades a màquines que coneixes (i pots verificar l'empremta per un altre canal); no escala a "qualsevol navegador contra qualsevol web". Curiositat per deixar el fil estès: SSH també admet certificats amb CA pròpies per a flotes grans de servidors — quan una organització creix, sempre acaba reinventant una PKI.
Errors Comuns i Consells
verify=Falsei la seva família (CERT_NONE,check_hostname=False,curl -k). Xifrar sense autenticar és xifrar cap a Mallory. Davant d'un error de certificat: diagnostica ambopenssl s_client, arregla la causa, mai el silenciïs.- Oblidar les intermèdies al servidor. El teu navegador potser ho tolera (cacheja intermèdies), però
requestso un altre backend fallarà. Serveix sempre elfullchain, i verifica ambs_clientque la seccióCertificate chainés completa. - Oblidar l'SNI a les proves. Sense
-servername(o senseserver_hostnamea Python), molts servidors retornen el certificat per defecte i creuràs que està trencat. Sis_clientet dona un certificat "estrany", revisa l'SNI abans de res. - Creure que "HTTPS" implica segur tot el sistema. TLS protegeix el trànsit. La base de dades, els logs, les còpies de seguretat (06-02) i l'aplicació continuen sent el teu problema — regla d'or núm. 9.
- Fixar versions o suites exòtiques a mà "per a més seguretat". Configuracions creatives trenquen clients i a vegades rebaixen la seguretat. Perfil Mozilla intermediate + test automatitzat.
- Acabar TLS a nginx i oblidar la xarxa interna. El
proxy_pass http://en clar és acceptable en localhost o xarxa estrictament controlada; entre hosts o zones de confiança diferents, també TLS (o mTLS) intern. Ho retrobem a 06-02.
Exercicis
Exercici 1 — Lectura de handshake. Connecta't amb openssl s_client (amb -servername) a dos llocs públics que facis servir i respon per a cadascun: (a) quina versió i cipher suite s'han negociat? (b) quantes baules ha enviat el servidor i qui és la intermèdia? (c) Verify return code? (d) accepta TLS 1.1? Justifica per què el resultat de (d) és l'esperable el 2026.
Exercici 2 — Caça del verify=False. Aquest codi heretat de MediNube consulta l'estat de les receptes a l'API de Farmàcia Robles, que fa servir un certificat emès per la CA interna compartida del consorci de farmàcies (/etc/medinube/pki/farmacias-ca.crt). Identifica tots els problemes i reescriu-lo correctament:
import requests, urllib3
urllib3.disable_warnings()
def estat_recepta_MALAMENT(id_recepta):
r = requests.get(
"https://api.farmacia-robles.example/recetas/" + id_recepta,
verify=False)
return r.json()Exercici 3 — El teu laboratori, servit per TLS. Amb el portal.crt/portal.key de la mini-PKI de 05-01, aixeca un servidor HTTPS local al port 8443 fent servir http.server + ssl.SSLContext de la stdlib, i verifica'l amb openssl s_client ... -CAfile ~/medinube-lab/pki/ca.crt, comprovant Verify return code: 0 (ok) i la versió negociada. (Pista: ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ctx.load_cert_chain(...) + ctx.wrap_socket(httpd.socket, server_side=True); afegeix 127.0.0.1 portal.medinube.example a /etc/hosts o fes servir -servername.)
Solucions
Solució 1. Exemple de sessió i lectura:
openssl s_client -connect ejemplo.com:443 -servername ejemplo.com </dev/null 2>/dev/null | grep -E "New,|Verify" # → New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 (a) # → Verify return code: 0 (ok) (c) openssl s_client -connect ejemplo.com:443 -servername ejemplo.com </dev/null 2>/dev/null | grep -A6 "Certificate chain" # (b): compta els parells s:/i: openssl s_client -connect ejemplo.com:443 -tls1_1 </dev/null # → normalment error tipus "no protocols available" o alerta del servidor (d)
(d) Ha de fallar: TLS 1.0/1.1 estan deprecades per l'RFC 8996 i retirades dels clients des de 2020; a més el teu propi binari d'openssl modern pot negar-se a intentar-ho. Que un servidor les accepti avui és una troballa d'auditoria.
Solució 2. Problemes: (1) verify=False: accepta qualsevol certificat — MalloryClinic pot respondre amb receptes falses o capturar els identificadors; (2) urllib3.disable_warnings(): silencia deliberadament l'avís que delatava el problema — el "fix" que amaga l'incendi; (3) sense timeout: una API penjada bloqueja el fil per sempre; (4) sense control d'errors HTTP; (5) l'error de fons era simplement que la CA del consorci no és pública: la solució és confiar en aquesta CA explícitament, no en totes ni en cap. Reescriptura:
import requests
CA_FARMACIES = "/etc/medinube/pki/farmacias-ca.crt"
def estat_recepta(id_recepta: str) -> dict:
r = requests.get(
f"https://api.farmacia-robles.example/recetas/{id_recepta}",
verify=CA_FARMACIES, # confiem NOMÉS en la CA del consorci
timeout=10,
)
r.raise_for_status()
return r.json()Solució 3.
import http.server
import ssl
httpd = http.server.HTTPServer(("0.0.0.0", 8443),
http.server.SimpleHTTPRequestHandler)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # rol servidor, versions segures
ctx.load_cert_chain(certfile="/home/el_teu_usuari/medinube-lab/pki/portal.crt",
keyfile="/home/el_teu_usuari/medinube-lab/pki/portal.key")
httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
print("Servint a https://localhost:8443 ...")
httpd.serve_forever()Verificació (l'SNI/-servername fa que el nom demanat coincideixi amb el SAN del certificat):
openssl s_client -connect localhost:8443 -servername portal.medinube.example \
-CAfile ~/medinube-lab/pki/ca.crt </dev/null 2>/dev/null | grep -E "New,|Verify"
# → New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
# → Verify return code: 0 (ok)Sense -CAfile obtindries el codi 19/20 ("self-signed certificate in certificate chain" / "unable to get local issuer"): la teva arrel de laboratori no és al trust store del sistema — i així ha de continuar; s'hi confia explícitament, crida a crida.
Conclusió
TLS era l'examen sorpresa que ja duies aprovat: ECDHE amb X25519 per acordar secrets amb forward secrecy (04-04), HKDF per derivar les claus de sessió (02-04), el certificat X.509 i la seva cadena per saber amb qui parles (05-01), una signatura CertificateVerify que demostra la possessió de la clau privada (04-03) i AEAD per a tot el trànsit (02-03) — empaquetat en un handshake d'una sola anada i tornada. Pel camí has après la part que no surt als llibres de teoria: llegir openssl s_client com un professional, desterrar verify=False i les seves disfresses del codi de MediNube, configurar nginx com a terminador TLS decent (1.2 mínim, 80→443, HSTS), reservar mTLS per a les integracions clínica↔MediNube i situar el model TOFU d'SSH davant de la PKI.
Queda l'última milla, la que distingeix un desplegament de cap de setmana d'un servei seriós: els certificats caduquen. Qui emet el de portal.medinube.example, qui el renova a les 4 de la matinada, qui se n'assabenta abans que caduqui, i què fem el dia que la clau privada aparegui on no hauria d'estar? Tot això és operació, té protocol propi (ACME) i fins i tot runbook d'incident. Ens veiem a 05-03: Gestió del Cicle de Vida de Certificats, la lliçó que tanca el mòdul.
Curs de Criptografia Aplicada
Mòdul 1: Fonaments de la Criptografia
- Què és la criptografia i per a què serveix
- Codificació, ofuscació i xifratge
- Aleatorietat i entropia
- El principi de Kerckhoffs i les regles d'or
Mòdul 2: Criptografia Simètrica
- Xifratge simètric: AES i ChaCha20
- Modes d'operació
- Xifratge autenticat (AEAD)
- Derivació de claus (KDF)
Mòdul 3: Hashes, MAC i Contrasenyes
- Funcions hash criptogràfiques
- Autenticació de missatges amb HMAC
- Emmagatzematge segur de contrasenyes
Mòdul 4: Criptografia Asimètrica
- Fonaments de clau pública i RSA
- Criptografia de corba el·líptica
- Signatures digitals
- Intercanvi de claus: Diffie-Hellman
- Xifratge híbrid
Mòdul 5: PKI, Certificats i TLS
- Certificats X.509 i autoritats de certificació
- TLS a la pràctica
- Gestió del cicle de vida dels certificats
