Perché conta
Quando un server di posta consegna un messaggio a un altro, la sessione SMTP parte in chiaro sulla porta 25 e
solo dopo il comando STARTTLS (RFC 3207) passa a TLS. Il client non sa a priori se il destinatario lo
supporta, quindi se la negoziazione fallisce fa la cosa più “gentile”: consegna in chiaro. È cifratura
opportunistica, e per un attaccante on-path è sufficiente cancellare la riga 250-STARTTLS dalla risposta
del server per ottenere una sessione leggibile e modificabile. Nessun errore, nessun log sul mittente.
DANE risolve il problema ma richiede DNSSEC. MTA-STS (RFC 8461) offre lo stesso risultato appoggiandosi al modello WebPKI già noto: il destinatario pubblica via HTTPS una policy che dice “i miei MX supportano TLS con certificato valido, se non riesci a usarlo non consegnare”. TLS-RPT (RFC 8460) completa il quadro con report giornalieri sui fallimenti.
Come funziona
Il dominio example.com pubblica due cose: un record TXT che segnala l’esistenza della policy e un file
servito via HTTPS da un host dedicato, mta-sts.example.com.
sequenceDiagram
participant S as MTA mittente
participant D as DNS
participant W as mta-sts.example.com (HTTPS)
participant M as mx1.example.com
S->>D: TXT _mta-sts.example.com
D-->>S: "v=STSv1; id=20261027T0900"
S->>W: GET /.well-known/mta-sts.txt
W-->>S: mode: enforce, mx, max_age
Note over S: policy in cache per max_age secondi
S->>M: EHLO + STARTTLS
M-->>S: certificato per mx1.example.com
Note over S: nome MX in policy e certificato valido?<br/>se no: non consegna, riprova più tardi
S->>M: DATA (cifrato)
Il campo id nel TXT serve solo a segnalare che la policy è cambiata: il mittente la riscarica quando
l’id differisce da quello in cache. Il file di policy è testo semplice:
|
|
max_age è in secondi: $604800 = 7 \times 86400$, cioè sette giorni. Questo numero è il vero parametro di
sicurezza. Un attaccante che blocca o falsifica il TXT dopo che il mittente ha già la policy in cache non
ottiene nulla per $T_{cache} = max_age$ secondi; ma il primo contatto resta non protetto (modello TOFU,
Trust On First Use), come ammette l’RFC. Più max_age è alto, più a lungo dura la protezione dopo ogni
fetch riuscito, ma più lento è il rollback se sbagli la configurazione.
L’attacco
Il downgrade si dimostra in laboratorio con un MITM che filtra l’annuncio STARTTLS. Con nftables e un
proxy trasparente, oppure più semplicemente con uno script scapy/mitmproxy che riscrive la risposta
EHLO, il client vede questo:
|
|
Una seconda variante è il MX falso: l’attaccante avvelena la risposta DNS per MX example.com e punta a un
server che presenta un certificato qualsiasi, perché i client SMTP opportunistici di norma non validano
il certificato (spesso è autofirmato anche per i server legittimi). Entrambi gli scenari sono invisibili senza
una policy esplicita.
La difesa
Lato DNS si pubblicano i due record (il secondo abilita TLS-RPT):
|
|
L’host mta-sts.example.com deve avere un certificato WebPKI valido e servire il file esattamente su
/.well-known/mta-sts.txt. Con nginx:
|
|
Il percorso consigliato è graduale: parti con mode: testing (i mittenti consegnano comunque ma inviano
report TLS-RPT sui problemi), osserva i report per qualche settimana, poi passa a mode: enforce alzando
max_age fino a settimane. Ogni volta che cambi la policy devi cambiare l’id nel TXT, altrimenti i
mittenti continuano a usare la copia in cache.
Per la parte in ricezione basta che i tuoi MX abbiano un certificato valido il cui nome coincida con una
voce mx: (sono ammessi wildcard come *.example.com). Per la parte in invio, Postfix non implementa
MTA-STS in modo nativo: si usa il demone postfix-mta-sts-resolver
come smtp_tls_policy_maps, che traduce la policy in una regola secure/encrypt per ogni dominio.
Il report TLS-RPT arriva come JSON compresso: conta sessioni riuscite e fallite, con motivo
(certificate-expired, starttls-not-supported, sts-policy-invalid…). Un picco di
starttls-not-supported per un solo mittente è esattamente la firma di un downgrade in corso.
Lab
Obiettivo: verificare che un dominio di test applichi la policy. Con containerlab crea un mittente Postfix, un MX e un nodo MITM in mezzo.
- Pubblica policy
mode: testinge TXT_mta-stssu un dominio di prova. - Dal MITM filtra
250-STARTTLSe invia una mail: consegna avvenuta, ma cosa trovi nei report TLS-RPT? - Passa a
mode: enforce, cambia l’ide ripeti. Cosa cambia e perché?
Soluzione
In testing il mittente consegna comunque in chiaro (la policy è solo osservata) ma al report
successivo trovi un record starttls-not-supported contro il tuo MX: è il segnale che qualcuno,
o qualcosa, ha rimosso l'annuncio. Passando a enforce e cambiando l'id, il mittente
riscarica la policy; ora, non vedendo STARTTLS, rifiuta di consegnare e mette il messaggio in coda con errore
temporaneo, riprovando fino al timeout della coda. Se la causa era un MITM, il messaggio non viene mai
consegnato in chiaro; se era un tuo MX mal configurato, hai appena causato un ritardo di posta: per questo
si osserva sempre prima in testing.
Conclusione
MTA-STS non è perfetto: il primo contatto resta vulnerabile e dipende dalla WebPKI. Ma costa un record DNS,
un file statico e un certificato, e trasforma un downgrade silenzioso in un errore visibile. Abbinalo sempre
a TLS-RPT, parti in testing, cambia l’id a ogni modifica e valuta DANE se hai già DNSSEC.
Articoli correlati: DANE e TLSA · SPF, DKIM e DMARC · TLS in profondità